home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
nt
/
ibmsyn.zip
/
SEMFISDD.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-13
|
202KB
|
4,275 lines
/* semfisdd.c */
/* Device Driver for IBM SDLC adapter. */
#ifndef IMADRIVER
#error - driver code requires IMADRIVER be defined on command line
#endif
#include <stddef.h>
#include <ntddk.h>
#include <semfismi.h>
#include <semfistr.h>
#include <semfisnt.h>
#include <semfisdm.h>
#include <semfis73.h>
#include <semfissp.h>
#include <semfisis.h>
#include <semfisrb.h>
#include <semfisfs.h>
#include <seclink.h>
#include <semfisdo.h>
#include <semfispr.h>
#include <semfisda.h>
#include <semfisel.h>
// Temporary Usage : function prototype declaration should be pulled in
extern NTSTATUS
ZwOpenKey(
OUT PHANDLE KeyHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
);
/* semfiddd.c - NT Device Driver for IBM SDLC adapter */
/**PROC+**********************************************************************/
/* */
/* Name AdapterExistenceCheck ( */
/* PCONFIGDATA pConfigData */
/* ); */
/* */
/* Purpose Hardware-specific of initialising */
/* */
/* Params IN pConfigData: the config data record for adapter to be checked*/
/* */
/* Return Value BOOLean: TRUE if the adapter is present and responding */
/* */
/* Side Effect: For MPAA, sets dma channel in config data! This will then */
/* immediately be copied to the Device Object config data ( so */
/* no danger of it being used by another configdata record in */
/* error. */
/* */
/* Operation: Tries to read ports confirming the presence of the adapter */
/* This is a trivial check of one port, suitable for start of */
/* day checks just to tell us if the device should be created. */
/* Real hardware initialisation is done on open. */
/* */
/* Notes We do as little as we can get away with here because a) we */
/* don't have a full device extension set up and b) to avoid */
/* causing interrupts hopefully. */
/* */
/**PROC-**********************************************************************/
BOOLean AdapterExistenceCheck (
PCONFIGDATA pConfigData
)
{
BOOLean FoundMPA;
if (pConfigData->AdapterType EQ AT_SDLC ||
pConfigData->AdapterType EQ AT_MPCA1 ||
pConfigData->AdapterType EQ AT_MPCA2)
{
if (pConfigData->MPCAModePort NE 0)
{
/***********************************************************************/
/* This is an MPCA adapter, we hope. Disable and reenable it */
/***********************************************************************/
WR_N_DELAY (pConfigData->MPCAModePort, AC_MPCAD);
WR_N_DELAY (pConfigData->MPCAModePort, pConfigData->MPCAModeValue);
TRACE_DATABYTE (Mpc, pConfigData->MPCAModeValue);
}
/*************************************************************************/
/* There isn't a reliable way of determining if the MPCA/SDLC cards */
/* are present. So at this stage say they are and let non-existence */
/* show up later as an Open error. */
/*************************************************************************/
return (TRUE);
}
if (pConfigData->AdapterType EQ AT_MPAA1 ||
pConfigData->AdapterType EQ AT_MPAA2)
{
IO_ADDRESS AdapterSelector;
for (
FoundMPA = FALSE,
AdapterSelector = (IO_ADDRESS) 8;
AdapterSelector < (IO_ADDRESS) 0x10;
AdapterSelector++
)
{
WR_N_DELAY (POS_Adapter_Select, (UCHAR) AdapterSelector);
if (IO_IN (POS_AdapterID_HiByte) EQ HIBYTE(POS_MPAATypeID) &&
IO_IN (POS_AdapterID_LoByte) EQ LOBYTE(POS_MPAATypeID) &&
((UCHAR)
(IO_IN(POS_ConfigByte2) & 0x1F)
) EQ pConfigData->MPAAAdapterIdentifier)
{
FoundMPA = TRUE;
pConfigData->DMAChannel = (UCHAR) (IO_IN (POS_ConfigByte3) & 7);
break;
}
}
WR_N_DELAY (POS_Adapter_Select, 7);
return (FoundMPA);
}
}
/*****************************************************************************/
/* */
/* Name AdapterReset */
/* */
/* Purpose Reset an adapter and prime it */
/* */
/* Params IN pDX */
/* */
/* OUT None */
/* */
/* Modified: 31/03/88 Initial coding */
/* */
/*****************************************************************************/
void AdapterReset (PDX pDX)
{
TRACE_EVENT (<ARs); /* every body needs one... */
pDX->HardwareError = FALSE; /* if user screwed up on hardware */
/* previously, we'll let him try */
/* again this time */
/***************************************************************************/
/* If an MPCA has been configured then write out the mode set byte. */
/***************************************************************************/
if (pDX->ConfigData.MPCAModePort NE 0)
{
WR_N_DELAY (pDX->ConfigData.MPCAModePort, pDX->ConfigData.MPCAModeValue);
TRACE_DATABYTE (Mpc, pDX->ConfigData.MPCAModeValue);
}
/***************************************************************************/
/* Now mode set the 8255. */
/***************************************************************************/
WR_N_DELAY (pDX->ADAPTERBASE+AR_8255, A55_ModeSet);
IO_OUT (pDX->ADAPTERBASE+AR_8255B,A55_Reset8273On);
KeStallExecutionProcessor (10L); /* a long delay for reset on */
WR_N_DELAY (pDX->ADAPTERBASE+AR_8255B,A55_Reset8273Off);
IO_OUT (pDX->ADAPTERBASE+AR_8255C, A55_InitPortC);
TRACE_EVENT (ARs>);
}
/**PROC+**********************************************************************/
/* */
/* Name AllocateDMAMemory ( */
/* int BufferSize */
/* PVOID *pBufferPtr */
/* PMDL *pMdl */
/* PHYSICAL_A *pPhysAddr */
/* ULONG *ErrorInformation */
/* ); */
/* */
/* Purpose Modularise the steps to allocate/deallocate and check DMA mem*/
/* Also, make freeing up easy (like, free everything on failure)*/
/* */
/* Params IN BufferSize */
/* OUT BufferPtr pointer to actual memory */
/* OUT pMdl the allocated mdl */
/* OUT pPhysAddr physical memory's address */
/* OUT ErrorInformation: suitable for IoStatus.Information */
/* */
/* Return Value BOOLean: TRUE if everything allocated OK */
/* */
/**PROC-**********************************************************************/
BOOLean AllocateDMAMemory (
ULONG BufferSize,
PVOID *pBufferPtr,
PMDL *ppMdl,
PHYSICAL_ADDRESS * pPhysAddr,
ULONG *ErrorInformation
)
{
BOOLean ReturnCode = TRUE;
TRACE_EVENT (<Dma);
*pBufferPtr = NULL;
*ppMdl = NULL;
*pBufferPtr = MmAllocateContiguousMemory (BufferSize,
RtlConvertUlongToLargeInteger (0xFFFFFFL) );
if (*pBufferPtr EQ NULL)
{
ReturnCode = FALSE;
*ErrorInformation = IO_ERR_CANT_ALLOCATE_MEMORY;
}
if (ReturnCode)
{
*ppMdl = IoAllocateMdl(*pBufferPtr,
BufferSize,
FALSE, /* this is not a secondary buffer */
FALSE, /* no charge to quota */
(PIRP)NULL /* not attached to IRP */
);
if (*ppMdl EQ NULL)
{
ReturnCode = FALSE;
*ErrorInformation = IO_ERR_CANT_ALLOCATE_MDL;
}
}
if (ReturnCode)
{
MmProbeAndLockPages (*ppMdl, KernelMode, IoModifyAccess);
*pPhysAddr = MmGetPhysicalAddress(*pBufferPtr);
ASSERT ((pPhysAddr->LowPart | pPhysAddr->HighPart) NE 0L);
/* check our understanding of MmAllocateContiguousMemory */
ASSERT (BITSOFF(pPhysAddr->LowPart, 0xFF000000L));
if (DMACrosses64K(pPhysAddr->LowPart, BufferSize))
{
ReturnCode = FALSE;
*ErrorInformation = IO_ERR_DMA_BUFFER_UNUSABLE;
}
}
if (!ReturnCode)
{
if (*ppMdl NE NULL)
{
IoFreeMdl (*ppMdl);
*ppMdl = NULL;
}
if (*pBufferPtr NE NULL)
{
MmFreeContiguousMemory (*pBufferPtr);
*pBufferPtr = NULL;
}
}
TRACE_EVENT (Dma>);
return (ReturnCode);
}
/**PROC+**********************************************************************/
/* */
/* Name: Close8273Sequence (PDX pDX) */
/* */
/* Purpose: To gracefully close down the 8273 */
/* */
/* Params: IN pDX */
/* */
/* Implicit input: Must not be called at interrupt or Synchronised level!!! */
/* */
/* Return Value:none */
/* */
/**PROC-**********************************************************************/
void Close8273Sequence (PDX pDX)
{
TRACE_EVENT (<C73);
/*************************************************************************/
/* Clear down the transmitter and receiver and release the h/w. */
/*************************************************************************/
IoctlAbortTransmitter (pDX);
IoctlAbortReceiver (pDX);
KeSynchronizeExecution(pDX->Interrupt, SynchReset8273, (PVOID) pDX);
/*************************************************************************/
/* Drop back down to non-interrupt level to allow pending interrupts in. */
/* The SynchReset8273 call above sets the Closing flag */
/*************************************************************************/
KeSynchronizeExecution(pDX->Interrupt, SynchTerminateAdapter, (PVOID) pDX);
pDX->AdapterIsClosing = FALSE;
TRACE_EVENT (C73>);
}
/**PROC+**********************************************************************/
/* */
/* Name: CompleteIoRequest (PIRP pIrp) */
/* */
/* Purpose: The Io request must be completed for each entry point. */
/* */
/* Params: IN pIrp The Io Request to be completed */
/* */
/* Return Value:none */
/* */
/* Operation: The Io request must be completed at despatch level */
/* */
/**PROC-**********************************************************************/
void CompleteIoRequest (PIRP pIrp)
{
/***************************************************************************/
/* IoCompleteRequest used to need a RaiseIrql ... but no longer does. */
/* Keep this as a subroutine for now - could become a macro later. */
/***************************************************************************/
//KIRQL PreviousIrql;
//KeRaiseIrql (DISPATCH_LEVEL, &PreviousIrql);
IoCompleteRequest (pIrp, 0); /* 0 => no priority boost for APC */
//KeLowerIrql (PreviousIrql);
TRACE_EVENT (S+I:);
TRACE_DWORD (pIrp->IoStatus.Status);
TRACE_DWORD (pIrp->IoStatus.Information);
}
/**PROC+**********************************************************************/
/* */
/* Name: DeviceInit ( */
/* PDRIVER_OBJECT DriverObject */
/* CHAR * DeviceName, */
/* PCONFIGDATA pConfigData, */
/* ) */
/* */
/* Purpose: Initialise the next device (from GetDriverSpec) */
/* Called for each configured device from DriverEntry. */
/* */
/* Params: IN DriverObject: Us! */
/* IN DeviceName: COMDL$0x (from GetDriverSpec()) */
/* IN pConfigData: config data to use */
/* */
/* Return Value:BOOLean: TRUE if device successfully initialised */
/* */
/* Operation: The general principle is: do as little as possible to here - */
/* just establish the device exists and allocate a device */
/* object for it. Any allocation of resources is done on open */
/* to avoid using resources unnecessarily. This applies to */
/* interrupt objects, */
/* */
/* 0. On input, driver and flavour name read from cfg register */
/* */
/* 1. If */
/* |--> HardwareExistenceCheck */
/* fails, */
/* return failure */
/* */
/* 2. Init for NT */
/* - allocate DeviceObject */
/* - DO_DIRECT_IO */
/* - IoInitializeDpcRequest */
/* */
/* 3. Init the device extension fields we need to know now: */
/* - DeviceIsOpen = FALSE */
/* - pointer to config data */
/* */
/* Note: The parallel driver returns an NT status from an equivalent */
/* routine. Eventually, we may do the same via ErrorCode param.*/
/* But for now we don't set error code, because the NTSTATUS */
/* returned from the parallel driver routine is ignored. */
/* */
/**PROC-**********************************************************************/
BOOLean DeviceInit (
PDRIVER_OBJECT pDriverObject,
CHAR * DeviceName,
PCONFIGDATA pConfigData
)
{
STRING NameString; /* counted string for NT */
PDEVICE_OBJECT pDeviceObject; /* allocated by NT for our device */
PDX pDX; /* pointer to our device extension */
NTSTATUS Status;
UNICODE_STRING UniNameString; /* same thing in UniCode */
if (!AdapterExistenceCheck(pConfigData))
{
TRACE_EVENT (DiNa); /* DeviceInit: No adapter */
DEBUG_PRINT (("IBMSYNC: Existence check failed for Adapter %s\n",
pConfigData->FlavourName));
return (FALSE); /* no device so can't initialise devc*/
}
RtlInitString (&NameString, DeviceName);
Status = RtlAnsiStringToUnicodeString (&UniNameString, &NameString, TRUE);
ASSERT(NT_SUCCESS(Status)); /* UniNameString now has unicode dvnm*/
Status = IoCreateDevice(
pDriverObject,
sizeof( IBMSYNC_DEVICE_EXTENSION ),
&UniNameString,
FILE_DEVICE_DATALINK,
0,
TRUE, /* open exclusively */
&pDeviceObject
);
RtlFreeUnicodeString (&UniNameString);
/***************************************************************************/
/* Note that we cannot log an error here since we do not hace a Device */
/* Object. */
/***************************************************************************/
if (!NT_SUCCESS(Status))
{
TRACE_EVENT (DiNd); /* DeviceInit: No device */
DEBUG_PRINT (("IBMSYNC: Cannot create Device : %s\n", DeviceName));
return (FALSE);
}
/***************************************************************************/
/* We have a device object OK - which is the main thing to do at device */
/* init time. Other initialisation stuff we leave till device opened. */
/***************************************************************************/
pDeviceObject->Flags |=DO_DIRECT_IO;
pDX = pDeviceObject->DeviceExtension;
pDX->DeviceIsOpen = FALSE;
pDX->AdapterIsClosing = FALSE;
pDX->PowerFailed = FALSE;
/***************************************************************************/
/* In theory, we don't have to do this config data copying (we could just */
/* use a pointer to it, but that would be a) slower; b) tedious to code */
/***************************************************************************/
pDX->pDeviceObject = pDeviceObject;
pDX->ConfigData = *pConfigData;
pDX->Name[0] = 'A'; /* adapter */
pDX->Name[1] = pConfigData->FlavourName[4];
IoInitializeDpcRequest(pDeviceObject, DPCRoutine);
DEBUG_PRINT (("IBMSYNC: Created Device : %s\n", DeviceName));
return (TRUE);
}
/**PROC+**********************************************************************/
/* */
/* Name: DPCRoutine */
/* */
/* Purpose: */
/* */
/* Params: */
/* */
/* Return Value:None */
/* */
/**PROC-**********************************************************************/
VOID DPCRoutine(
IN PKDPC pDpc,
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID DeferredContext
)
{
PDX pDX = pDeviceObject->DeviceExtension;
TRACE_EVENT (<DPC);
UNREFERENCED_PARAMETER (pDpc);
UNREFERENCED_PARAMETER (pIrp);
UNREFERENCED_PARAMETER (DeferredContext);
ASSERT (pDX->DeviceIsOpen);
if (pDX->DeviceIsOpen &&
pDX->DPCAction & DPC_ACTION_PULSE &&
pDX->pUserEvent NE NULL)
{
// KePulseEvent (pDX->pUserEvent, 0, FALSE); /* 0 pri boost, no wait */
// KePulseEvent missing from API?
KeSetEvent (pDX->pUserEvent, 0, FALSE);
}
pDX->DPCAction = 0; /* we've done all requested actions */
TRACE_EVENT (DPC>);
}
/**PROC+**********************************************************************/
/* */
/* Name: DriverEntry( */
/* PDRIVER_OBJECT DriverObject */
/* IN PUNICODE_STRING RegistryPath */
/* ) */
/* */
/* Purpose: As defined by NT. Called once only when NT loads driver */
/* */
/* Params: PDRIVER_OBJECT: pointer to us */
/* */
/* Return Value:NTSTATUS: STATUS_SUCCESS / STATUS_UNSUCCESSFUL */
/* Unsuccessful if no devices initialised */
/* */
/**PROC-**********************************************************************/
NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
CHAR DeviceName [80];
BOOLean GotADevice = FALSE;
NTSTATUS RetStatus = STATUS_UNSUCCESSFUL;
CONFIGDATA *pConfigData;
// _asm int 3;
TRACE_INIT();
TRACE_EVENT(Entr);
while (GetDriverSpec (DeviceName, &pConfigData))
{
if (DeviceInit (DriverObject, DeviceName, pConfigData))
{
GotADevice = TRUE;
TRACE_EVENT (DevI);
}
}
if ((GotADevice) && (GetInterfaceType (DriverObject)))
{
DriverObject->MajorFunction[IRP_MJ_CLOSE] = EntryPointClose;
DriverObject->MajorFunction[IRP_MJ_CREATE] = EntryPointOpen;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = EntryPointDevIoctl;
DriverObject->DriverUnload = EntryPointUnload;
RetStatus = STATUS_SUCCESS;
}
else
{
TRACE_EVENT (DrvF);
}
return (RetStatus);
}
/*****************************************************************************/
/* */
/* Name EntryPointClose */
/* */
/* Purpose Close Request Packet Handler */
/* */
/* This is the CLOSE processor. It flushes pending requests */
/* and ensures that the hardware and LCB fields are tidily */
/* closed down. */
/* */
/* Params IN None */
/* */
/* OUT Link hardware closed and pending requests flushed. */
/* */
/*****************************************************************************/
NTSTATUS EntryPointClose
(
PDEVICE_OBJECT pDeviceObject,
PIRP pIrp
)
{
PDX pDX = pDeviceObject->DeviceExtension;
NTSTATUS Status;
TRACE_NEWLINE();
TRACE_EVENT ({EPC);
pIrp->IoStatus.Information = 0;
if (pDX->DeviceIsOpen) /* use can close OK */
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
/*************************************************************************/
/* Clear down the transmitter and receiver and release the h/w. */
/*************************************************************************/
Close8273Sequence (pDX); /* orderly close-down of adapter */
IoDisconnectInterrupt (pDX->Interrupt);
if (pDX->ConfigData.Irql2)
{
IoDisconnectInterrupt (pDX->Interrupt2);
}
TRACE_EVENT (EPC1);
MmUnlockPages (pDX->RcvInfo.pRcvMdl);
TRACE_EVENT (EPC2);
IoFreeMdl (pDX->RcvInfo.pRcvMdl);
TRACE_EVENT (EPC3);
MmFreeContiguousMemory (pDX->RcvInfo.pRcvBufArray);
TRACE_EVENT (EPC4);
MmUnlockPages (pDX->pSendMdl);
TRACE_EVENT (EPC5);
IoFreeMdl (pDX->pSendMdl);
TRACE_EVENT (EPC6);
pDX->pSendMdl = NULL;
MmFreeContiguousMemory (pDX->pSendBuf);
TRACE_EVENT (EPC7);
pDX->pSendBuf = NULL;
TRACE_EVENT (EPC8);
// if (pDX->pIRMdl != NULL) /*IRMdl?*/
// { /*IRMdl?*/
// MmUnmapLockedPages (pDX->pIR, pDX->pIRMdl); /*IRMdl?*/
// MmUnlockPages (pDX->pIRMdl); /*IRMdl?*/
// TRACE_EVENT (EPC9); /*IRMdl?*/
// IoFreeMdl (pDX->pIRMdl); /*IRMdl?*/
// TRACE_EVENT (EPC0); /*IRMdl?*/
// pDX->pIRMdl = NULL; /*IRMdl?*/
// } /*IRMdl?*/
pDX->pIR = &pDX->OurIR;
pDX->GrabbedResources = 0;
pDX->DeviceIsOpen = FALSE;
pDX->pUserEvent = NULL;
}
else
{
pIrp->IoStatus.Status = STATUS_FILE_CLOSED;
}
Status = pIrp->IoStatus.Status;
CompleteIoRequest (pIrp);
TRACE_EVENT (EPC});
return (Status);
}
/**PROC+**********************************************************************/
/* */
/* Name: EntryPointDevIoctl ( */
/* PDEVICE_OBJECT pDeviceObject */
/* PIRP pIrp */
/* ) */
/* */
/* Purpose: Main ioctl entry point. */
/* */
/* Params: IN DeviceObject: Our device. */
/* IN PIrp The IRP in question. */
/* */
/* Return Value:depends on called routine */
/* */
/* Operation: */
/* */
/**PROC-**********************************************************************/
NTSTATUS EntryPointDevIoctl (
PDEVICE_OBJECT pDeviceObject,
PIRP pIrp
)
{
BOOLean OKToContinue = TRUE;
PDX pDX = pDeviceObject->DeviceExtension;
PIO_STACK_LOCATION
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
UCHAR * UserBufferPtr;
#ifdef IBMSYNC_TRACE
UCHAR * OriginalTrcPtr = TrcPtr;
BOOLEAN ResetTrcPtr = FALSE;
#endif
ASSERT (pDX->DeviceIsOpen); /* open before any Ioctls please */
TRACE_NEWLINE();
TRACE_EVENT({EPD);
TRACE_DWORD (pDeviceObject);
TRACE_DWORD (pDX);
TRACE_DWORD (pIrp);
TRACE_DWORD (pIrpSp->IRS_CODE);
TRACE_ACTION (Op,CAST(pDX->ADAPTERBASE,ULONG) & 0xFFFF);
pDX->IoctlRetStatus = STATUS_SUCCESS;
pDX->Information = 0L;
pDX->IoctlCurrentIrp= pIrp;
/***************************************************************************/
/* The subroutines return the success/fail indication as their return */
/* value, and additional information in pDX->Information */
/***************************************************************************/
switch (pIrpSp->IRS_CODE)
{
// case IoctlCodeSetInterfaceRecord /*IRMdl?*/
// : IoctlSetInterfaceRecord(pDX); /*IRMdl?*/
// break; /*IRMdl?*/
case IoctlCodeReadInterfaceRecord /*IRMdl?*/
: if (pIrpSp->IRS_OUTLEN NE sizeof(IR))
{
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_READ_IR_BUFFER_WRONG_SIZE;
}
else
{
#ifdef IBMSYNC_TRACE
ResetTrcPtr = TRUE;
#endif
UserBufferPtr = MmMapLockedPages
(pIrp->MdlAddress, KernelMode);
memcpy (UserBufferPtr, pDX->pIR, sizeof(IR));
}
break;
case IoctlCodeSetEvent : if (pIrp->UserEvent NE NULL)
{
pDX->pUserEvent = pIrp->UserEvent;
KeSetEvent (pDX->pUserEvent, 0, FALSE);
}
else
{
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_SET_EVENT_NO_EVENT;
}
break;
case IoctlCodeSetLinkChar : KeSynchronizeExecution
( pDX->Interrupt,
(PKSYNCHRONIZE_ROUTINE)
IoctlSetLinkConfig,
(PVOID) pDX
);
break;
case IoctlCodeRxFrame : KeSynchronizeExecution
( pDX->Interrupt,
(PKSYNCHRONIZE_ROUTINE) IoctlRxFrame,
(PVOID) pDX
);
break;
case IoctlCodeAbortReceiver : IoctlAbortReceiver (pDX);
break;
case IoctlCodeGetV24 : KeSynchronizeExecution
( pDX->Interrupt,
(PKSYNCHRONIZE_ROUTINE) GetV24Input,
(PVOID) pDX
);
/* data-> pDX->IR; no return status */
#ifdef IBMSYNC_TRACE
{
static UCHAR LastV24In = 0;
ResetTrcPtr = CAST(
pDX->pIR->V24In EQ LastV24In,
BOOLEAN);
LastV24In = pDX->pIR->V24In;
}
#endif
break;
case IoctlCodeTxFrame : KeSynchronizeExecution
( pDX->Interrupt,
(PKSYNCHRONIZE_ROUTINE) IoctlTxFrame,
(PVOID) pDX
);
break;
case IoctlCodeAbortTransmit : KeSynchronizeExecution
( pDX->Interrupt,
(PKSYNCHRONIZE_ROUTINE)
IoctlAbortTransmitter,
(PVOID) pDX
);
break;
case IoctlCodeSetV24 : if (!KeSynchronizeExecution
( pDX->Interrupt,
(PKSYNCHRONIZE_ROUTINE)
IoctlSetV24Output,
(PVOID) pDX
)
)
{
pDX->IoctlRetStatus = STATUS_DATA_ERROR;
pDX->Information = IO_ERR_HARDWARE_CMD_TIMEOUT_1;
}
break;
default:
// _asm int 3; // break into kernel debugger
pDX->IoctlRetStatus =
STATUS_INVALID_DEVICE_REQUEST;
pDX->Information = IO_ERR_INVALID_IOCTL_CODE;
}
if (NT_SUCCESS(pDX->IoctlRetStatus))
{
// The Ioctl worked OK, but how about 8273 commands - check the
// timeout flag, which is set by 8273 WAITUNTIL macro. Signal a fatal
// hardware error if it's set.
if (pDX->HardwareError)
{
pDX->IoctlRetStatus = STATUS_DATA_ERROR;
pDX->Information = IO_ERR_HARDWARE_CMD_TIMEOUT_2;
}
}
else
{
TRACE_NTFAILURE(pDX->IoctlRetStatus);
TRACE_ACTION (Ii,pDX->Information & 0xFFFF);
}
/***************************************************************************/
/* Note that at this point a non-zero value of pDX->Information does not */
/* mean an error - it could be a byte count of info returned in a buffer. */
/***************************************************************************/
if (!NT_SUCCESS(pDX->IoctlRetStatus))
{
LogDriverError( pDeviceObject,
pDX->IoctlRetStatus,
pDX->Information,
pIrpSp->MajorFunction,
pIrpSp->IRS_CODE );
}
pIrp->IoStatus.Status = pDX->IoctlRetStatus;
pIrp->IoStatus.Information = pDX->Information;
TRACE_EVENT(EPD});
CompleteIoRequest (pIrp);
#ifdef IBMSYNC_TRACE
if (ResetTrcPtr)
TrcPtr = OriginalTrcPtr;
#endif
return(pDX->IoctlRetStatus);
}
/**PROC+**********************************************************************/
/* */
/* Name: EntryPointISR ( */
/* PKINTERRUPT pInterrupt */
/* PVOID Context = pDeviceObject */
/* ) */
/* */
/* Purpose: Handle interrupt. */
/* */
/* Params: IN pInterrupt */
/* IN Context */
/* */
/* Return Value:BOOLean: TRUE if device successfully initialised */
/* */
/* Operation: */
/* */
/* Notes 1. The OS/2 device driver shares interrupts; this version does */
/* not because the interrupt object needs to be attached to a */
/* device. I hope we can get away it. */
/* 2. Return value is meant to indicate some powerfail stuff ... */
/* not bothering with it for now. */
/* */
/**PROC-**********************************************************************/
BOOLEAN EntryPointISR (
PKINTERRUPT pInterrupt,
PVOID Context
)
{
PDEVICE_OBJECT pDeviceObject= (PDEVICE_OBJECT) Context;
PDX pDX = pDeviceObject->DeviceExtension;
BOOLean OriginalDPCAction = pDX->DPCAction;
UCHAR Status; /* the 8273 status, not the NT Status*/
BOOLEAN ReturnValue;
UNREFERENCED_PARAMETER (pInterrupt);
//TRACE_NEWLINE();
//TRACE_EVENT (<ISR);
ASSERT (pDX != NULL);
//TRACE_OBJECT(Ad,(*pDX));
ASSERT (pDX->DeviceIsOpen);
if (pDX->AdapterIsClosing ||
!pDX->DeviceIsOpen)
{
// We reset the 8273 but there was an interrupt still pending... ignore it.
// This is safe because by resetting the 8273 we should have removed the
// cause of the interrupt.
return(TRUE);
}
ReturnValue = CAST ( \
(IO_IN (pDX->ADAPTERBASE+AR_8273S) & (AS_RXINT|AS_TXINT)) != 0,\
BOOLEAN \
); /* return true if we expected the interrupt */
do /* while we have an interrupt result avail. */
{
/*************************************************************************/
/* To be on the safe side, don't do anything further until the command */
/* busy bit in the status register has gone off. */
/*************************************************************************/
WAITUNTIL (pDX, AS_CMBSY, EQ, 0); /* wait for busy bit to go off */
Status = IO_IN (pDX->ADAPTERBASE+AR_8273S);
ASSERT (BITSOFF(Status, AS_CRBFF | AS_CPBFF | AS_CMBFF | AS_CMBSY));
if (Status & AS_RXINT) /* receiver action */
{
/***********************************************************************/
/* Note we don't stop DMA on receiver. The design of the buffers has */
/* taken into account receiving a full 8 frames+RR in DMA mode - so */
/* that we can take account of the 8273 feature of continuing to */
/* recieve when in DMA mode. */
/***********************************************************************/
if (Status & AS_RXIRA)
{
/*********************************************************************/
/* We have a receiver result. Read the result bytes into the */
/* device extension and after reading, action immediately */
/*********************************************************************/
for (pDX->RxResultCount = 0;
pDX->RxResultCount < (sizeof (pDX->RxResultBuffer) - 1);
/* last byte never gets filled in ! */
)
{
pDX->RxResultBuffer[pDX->RxResultCount++] =
IO_IN (pDX->ADAPTERBASE + AR_8273R);
KeStallExecutionProcessor (1L); /* let the chip settle down again*/
WAITUNTIL(pDX, (AS_RXIRA | AS_RXINT), NE, AS_RXINT);
TRACE_DATABYTE (RxR, pDX->RxResultBuffer[pDX->RxResultCount]);
/*******************************************************************/
/* either the INT bit went off or IRA went on */
/* If INT went off, ignore the rest - even if IRA went on */
/* (shouldn't have) */
/*******************************************************************/
if (BITSOFF(LastWaitUntilStatus, AS_RXINT))
break;
}
/*********************************************************************/
/* We do nothing if receiver is not active. This sitn. arises when */
/* */
/* - we prepare to disable the recver (start into synchronized execn)*/
/* - a result becomes available */
/* - the synchronized execution carries on and disables the receiver */
/* */
/* This only arises when we are in DMA mode - for SDLC. (For PIO, */
/* receiver kept active). Make life simple by ignoring result */
/* otherwise we get FSM's knickers in a twist. */
/*********************************************************************/
if (pDX->RxFSMCurState EQ RxFSMStateReady)
{
while (pDX->RxResultCount > 0)
{
/*****************************************************************/
/* There will either be one or two result groups set. The case */
/* of two groups arises when one result has occurred but not */
/* been read by the time another result comes in. */
/* */
/* The second result is only one byte so we special case this to */
/* make it easier get the rxresults down to the start again. */
/*****************************************************************/
if (pDX->RxResultBuffer[0] EQ ARxR_A1OK ||/* A1/A2 match, rcv OK */
pDX->RxResultBuffer[0] EQ ARxR_A2OK)
{
RxFSMEvent (pDX, RxFSMInputGoodReceive);
pDX->RxResultBuffer[0] = pDX->RxResultBuffer[5];
pDX->RxResultCount -= 5;
}
else
{
RxFSMEvent (pDX, RxFSMInputReceiverError);
pDX->RxResultBuffer[0] = pDX->RxResultBuffer[1];
pDX->RxResultCount -= 1;
}
}
}
}
else
{
/*********************************************************************/
/* IRA not set - no result available, must be for received character */
/* */
/* Not that neither here nor below in tx do check for overrunning */
/* the buffer - we just believe 8273 is OK (and it hasn't failed in */
/* this area yet. ASSERTions to guard against buffer overruns */
/* should be done in completion code. */
/*********************************************************************/
ASSERT (BITSOFF(pDX->GrabbedResources, GRABBEDRESOURCE_GOTDMA));
/* must be PIO then */
*(pDX->pRxPIOData++) = IO_IN (pDX->ADAPTERBASE+AR_8273D);
// TRACE_DATABYTE(RxD, *(pDX->pRxPIOData-1));
}
}
if (Status & AS_TXINT) /* transmitter action */
{
if (Status & AS_TXIRA)
{
/*********************************************************************/
/* We have a transmitter result. Read the result bytes into the */
/* device extension and after reading, request a DPC. */
/* */
/* The DPCAction tells the DPC why it is being called */
/* */
/* This implementation also assumes that only one transmitter result */
/* will be outstanding at any one time, so there is only one */
/* TxResult byte. */
/*********************************************************************/
pDX->TxResult = IO_IN (pDX->ADAPTERBASE + AR_8273T);
TRACE_DATABYTE (TxR, pDX->TxResult);
if (pDX->TxResult EQ ATxR_TxCompleteOK)
{
TxFSMEvent (pDX, TxFSMInputEOTx);
}
else if (pDX->TxResult EQ ATxR_ErrTxFrameAborted)
{
TxFSMEvent (pDX, TxFSMInputAbortd);
}
else
{
TxFSMEvent (pDX, TxFSMInputEOTx_Err);
}
}
else
{
/*********************************************************************/
/* IRA not set - no result available, must be to tx next character */
/*********************************************************************/
ASSERT (BITSOFF(pDX->GrabbedResources, GRABBEDRESOURCE_GOTDMA));
/* must be PIO then */
// TRACE_DATABYTE (TxD, *pDX->pTxPIOData);
IO_OUT(pDX->ADAPTERBASE+AR_8273D, *(pDX->pTxPIOData++));
}
}
Status = IO_IN (pDX->ADAPTERBASE + AR_8273S);
}
while (Status & (AS_TXINT|AS_RXINT));
if (!OriginalDPCAction && /* on entry, no dpc actions */
pDX->DPCAction) /* but on exit, we have DPC actions */
{
IoRequestDpc (pDeviceObject, NULL, NULL); /* DPC will pulse event for us */
}
//XTRACE_EVENT (ISR>);
return(ReturnValue);
}
/**PROC+**********************************************************************/
/* */
/* Name: EntryPointOpen ( */
/* PDEVICE_OBJECT pDeviceObject */
/* PIRP pIrp */
/* ) */
/* */
/* Purpose: Initialise the next device (from GetDriverSpec) */
/* */
/* Params: IN DeviceObject: Our device. */
/* IN PIrp The IRP in question. */
/* */
/* Return Value:BOOLean: TRUE if device successfully initialised */
/* */
/* Operation: */
/* 1. Copy over pre-initialised data sequences */
/* */
/* 2. Init for NT */
/* - allocate transmit and receive buffers DMA-able */
/* - DeviceObject */
/* - DO_DIRECT_IO */
/* - IoInitializeDpcRequest */
/* - ConnectInterrupt */
/* */
/* 3. Init our own data */
/* - DeviceIsOpen = FALSE */
/* */
/**PROC-**********************************************************************/
NTSTATUS EntryPointOpen (
PDEVICE_OBJECT pDeviceObject,
PIRP pIrp
)
{
//ULONG AddressSpace;
KAFFINITY Affinity;
int Information = 0;
KIRQL InterruptLevel;
CCHAR InterruptVector;
BOOLean OKToContinue = TRUE;
PDX pDX = pDeviceObject->DeviceExtension;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
int WindDownLevel= 0;
ASSERT (!pDX->DeviceIsOpen); /* device defined as exclusive usage!*/
TRACE_NEWLINE();
TRACE_EVENT({EPO);
TRACE_ACTION (Op,CAST(pDX->ADAPTERBASE, ULONG) & 0xFFFF);
pIrp->IoStatus.Information = 0L;
pDX->GrabbedResources = 0; /* clear the list of resources */
if (InterfaceType == MicroChannel)
{
if (!pDX->ConfigData.MPAAAdapterIdentifier)
{
OKToContinue = FALSE;
Status = STATUS_INSUFFICIENT_RESOURCES;
Information = IO_ERR_NEEDS_ISA_BUS;
TRACE_EVENT (OcWb);
}
}
else
{
if (pDX->ConfigData.MPAAAdapterIdentifier)
{
OKToContinue = FALSE;
Status = STATUS_INSUFFICIENT_RESOURCES;
Information = IO_ERR_NEEDS_MCA_BUS;
TRACE_EVENT (OcWb);
}
}
//
// Map the memory for the control registers for the parallel device
// into virtual memory. This code needs to be activated when moving to
// a platform with memory-mapped I/O.
//
// AddressSpace = pDX->ConfigData.AddressSpace;
// CardAddress = HalTranslateBusAddress(
// InterfaceType,
// pDX->ConfigData->BusNumber,
// pDX->ConfigData->AdapterBaseAddress,
// &AddressSpace
// );
//
// if (!AddressSpace)
// {
// ParDeviceObject->UnMapRegisters = TRUE;
// ParDeviceObject->DeviceRegisters = MmMapIoSpace(
// CardAddress,
// PARALLEL_REGISTER_LENGTH,
// FALSE
// );
//
// } else {
//
// ParDeviceObject->UnMapRegisters = FALSE;
// ParDeviceObject->DeviceRegisters = (PVOID)CardAddress.LowPart;
//
// }
//
// if (!ParDeviceObject->DeviceRegisters) {
//
// DbgPrint("Couldn't map the device registers.\n");
// IoDeleteDevice(DeviceObject);
// return STATUS_NONE_MAPPED;
//
// }
/***************************************************************************/
/* 1. NT-related device initialisation */
/***************************************************************************/
if (OKToContinue)
{
InterruptVector = HalGetInterruptVector(
InterfaceType,
pDX->ConfigData.BusNumber,
pDX->ConfigData.Irql,
pDX->ConfigData.Vector,
&InterruptLevel,
&Affinity
);
Status = IoConnectInterrupt (
&pDX->Interrupt,
EntryPointISR,
pDeviceObject,
NULL,
InterruptVector,
InterruptLevel, /* interrupt IRQ level */
InterruptLevel, /* synchronize IRQ level */
pDX->ConfigData.InterruptMode,
pDX->ConfigData.Shareable,
Affinity, /* processor number */
FALSE /* no save floating pt contxt*/
);
if (!NT_SUCCESS(Status))
{
OKToContinue = FALSE;
Status = STATUS_INSUFFICIENT_RESOURCES;
Information = IO_ERR_CANT_CONNECT_INTERRUPT_1;
TRACE_EVENT (OcCi);
}
}
if (OKToContinue)
{
WindDownLevel = 15;
if (pDX->ConfigData.Irql2)
{
/***********************************************************************/
/* A second interrupt is required */
/***********************************************************************/
ASSERT (pDX->ConfigData.FlavourName[0] EQ 'S');
/* only the SDLC card is so dumb */
if (OKToContinue)
{
InterruptVector = HalGetInterruptVector(
InterfaceType,
pDX->ConfigData.BusNumber,
pDX->ConfigData.Irql2,
pDX->ConfigData.Vector2,
&InterruptLevel,
&Affinity
);
Status = IoConnectInterrupt (
&pDX->Interrupt2,
EntryPointRogueInterrupt,
pDeviceObject,
NULL,
InterruptVector,
InterruptLevel, /* interrupt IRQ level */
InterruptLevel, /* synchronize IRQ level */
pDX->ConfigData.InterruptMode2,
pDX->ConfigData.Shareable2,
1, /* processor number */
FALSE /* no save floating pt contxt*/
);
if (!NT_SUCCESS(Status))
{
OKToContinue = FALSE;
Status = STATUS_INSUFFICIENT_RESOURCES;
Information = IO_ERR_CANT_CONNECT_INTERRUPT_2;
TRACE_EVENT (OcCi);
}
}
}
}
/***************************************************************************/
/* 2. Buffer allocation and init */
/***************************************************************************/
if (OKToContinue)
{
WindDownLevel = 20;
OKToContinue = AllocateDMAMemory (sizeof(RCVBUFARRAY),
(PVOID *)&pDX->RcvInfo.pRcvBufArray,
&pDX->RcvInfo.pRcvMdl,
&pDX->RcvInfo.RcvBufPhysAddr,
&Information
);
if (!OKToContinue)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
// Information set in above call to AllocateDmaMemory
}
}
if (OKToContinue)
{
WindDownLevel = 30;
RCVINFO_INIT(pDX);
pDX->RxFSMCurState = RxFSMStateIdle;
OKToContinue = AllocateDMAMemory (SENDBUF_SIZE,
(PVOID *)&pDX->pSendBuf,
&pDX->pSendMdl,
&pDX->SendBufPhysAddr,
&Information
);
if (!OKToContinue)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
// Information set in above call to AllocateDmaMemory
}
}
if (OKToContinue)
{
WindDownLevel = 40;
pDX->TxFSMCurState = TxFSMStateIdle;
pDX->TxConsecutiveUnderrunCount = 0;
COPY8273CMD (pDX, CmdStringReadPortA );
COPY8273CMD (pDX, CmdStringResetOpMode );
COPY8273CMD (pDX, CmdStringResetSerialIOMode);
COPY8273CMD (pDX, CmdStringSetOpMode );
COPY8273CMD (pDX, CmdStringSetSerialIOMode );
COPY8273CMD (pDX, CmdStringDataTransferMode );
COPY8273CMD (pDX, CmdStringResetPortB );
COPY8273CMD (pDX, CmdStringSetPortB );
COPY8273CMD (pDX, CmdStringReceive );
COPY8273CMD (pDX, CmdStringTransmit );
COPY8273CMD (pDX, CmdStringAbortTransmit );
COPY8273CMD (pDX, CmdStringDisableReceiver );
pDX->pIR = &pDX->OurIR; /* use dummy IR for now until real */
pDX->pIR->V24In = 0;
pDX->pIR->V24Out = 0;
pDX->pIR->RxFrameCount= 0;
pDX->pIR->TxMaxFrSizeNow = INIT_MAXFRSIZENOW;
// pDX->pIRMdl = NULL; /*IRMdl?*/
pDX->pUserEvent = NULL; /* null until Ioctl: SetEvent */
}
/***************************************************************************/
/* SDLC device initialisation */
/***************************************************************************/
if (OKToContinue)
{
if (!KeSynchronizeExecution(pDX->Interrupt,
SynchEntryPointOpen,
(PVOID)pDX))
{
Status = STATUS_DATA_ERROR;
Information = pDX->Information;
OKToContinue = FALSE;
}
}
if (OKToContinue)
{
if (pDX->HardwareError)
{
Status = STATUS_DATA_ERROR;
Information = IO_ERR_HARDWARE_CMD_TIMEOUT_3;
OKToContinue = FALSE;
}
}
if (!OKToContinue)
{
TRACE_ACTION (Wl, WindDownLevel & 0xFFFF);
if (WindDownLevel >= 15)
IoDisconnectInterrupt( pDX->Interrupt);
if (pDX->ConfigData.Irql2)
{
if (WindDownLevel >= 20)
IoDisconnectInterrupt( pDX->Interrupt2);
}
if (WindDownLevel >= 30)
{
MmUnlockPages (pDX->RcvInfo.pRcvMdl);
IoFreeMdl (pDX->RcvInfo.pRcvMdl);
MmFreeContiguousMemory (pDX->RcvInfo.pRcvBufArray);
}
if (WindDownLevel >= 40)
{
MmUnlockPages (pDX->pSendMdl);
IoFreeMdl (pDX->pSendMdl);
pDX->pSendMdl = NULL;
MmFreeContiguousMemory (pDX->pSendBuf);
pDX->pSendBuf = NULL;
}
LogDriverError( pDeviceObject,
Status,
Information,
IoGetCurrentIrpStackLocation(pIrp)->MajorFunction,
0L );
}
pIrp->IoStatus.Status = Status;
pIrp->IoStatus.Information = Information;
if (Status EQ STATUS_SUCCESS)
{
pDX->DeviceIsOpen = TRUE;
}
TRACE_EVENT(EPO});
TRACE_NTFAILURE (Status);
CompleteIoRequest (pIrp);
return (Status);
}
/*****************************************************************************/
/* */
/* Name EntryPointRogueInterrupt */
/* */
/* Purpose Mode Status ISR. */
/* */
/* Should never be called!!! This ISR is used to handle int4 */
/* on SDLC cards (modem status and timers). Since the int is */
/* explicitly masked off in adapter set-up, it is a severe err */
/* if we actually get one! */
/* */
/* */
/* Params IN None */
/* */
/* OUT DLC appl hardware status updated. */
/* */
/*****************************************************************************/
BOOLEAN EntryPointRogueInterrupt (
PKINTERRUPT pInterrupt,
PVOID Context
)
{
PDEVICE_OBJECT pDeviceObject= (PDEVICE_OBJECT) Context;
PDX pDX = pDeviceObject->DeviceExtension;
UNREFERENCED_PARAMETER (pInterrupt);
TRACE_EVENT ({EP?);
/***************************************************************************/
/* Update hardware error status and trigger an event for Link process via */
/* DPC */
/***************************************************************************/
pDX->pIR->StatusArray[SA_HardwareError]++;
pDX->pIR->StatusCount++;
pDX->DPCAction |= DPC_ACTION_PULSE;
IoRequestDpc (pDeviceObject, NULL, NULL); /* DPC will pulse event for us */
/***************************************************************************/
/* Reset the modem status logic and turn the 8273 off - it is being too */
/* disruptive to keep active! */
/***************************************************************************/
WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_ResetModemStatusCh+
A55_Reset8273Off);
WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273Off);
TRACE_EVENT (EP?});
return(FALSE); /* interrupt wasn't expected */
}
/**PROC+**********************************************************************/
/* */
/* Name: EntryPointUnload ( */
/* PDRIVER_OBJECT pDriverObject */
/* ) */
/* */
/* Purpose: Unload the device - don't need to do anything? */
/* */
/* Params: IN DriverObject: Our device. */
/* */
/* Return Value:None */
/* */
/* Operation: */
/* */
/**PROC-**********************************************************************/
VOID
EntryPointUnload (
IN PDRIVER_OBJECT pDriverObject
)
{
IoDeleteDevice( pDriverObject->DeviceObject );
return;
}
/**PROC+**********************************************************************/
/* */
/* Name GetInterfaceType ( */
/* PDRIVER_OBJECT pDriverObject */
/* ) */
/* */
/* Purpose Hide the vagaries of roasting squirrels */
/* */
/* Return Value BOOLean : TRUE if InterfaceType (global) set up OK */
/* */
/**PROC-**********************************************************************/
BOOLean GetInterfaceType (
IN PDRIVER_OBJECT pDriverObject
)
{
PUNICODE_STRING RegistryPath = pDriverObject->HardwareDatabase;
PRTL_QUERY_REGISTRY_TABLE Parameters = NULL;
UNICODE_STRING ParametersPath;
OBJECT_ATTRIBUTES ParametersAttributes;
HANDLE ParametersKey;
UNICODE_STRING Identifier;
UNICODE_STRING MCAString;
NTSTATUS Status = STATUS_SUCCESS;
ULONG Information;
TRACE_EVENT([GIT);
Identifier.Buffer = NULL;
/***************************************************************************/
/* Set up the Registry path to check if \EisaAdapter\0 key exists. */
/***************************************************************************/
RtlInitUnicodeString (&ParametersPath, NULL);
ParametersPath.MaximumLength = RegistryPath->Length +
sizeof(L"\\MultifunctionAdapter\\0") +
4;
ParametersPath.Buffer = ExAllocatePool(
PagedPool,
ParametersPath.MaximumLength
);
if (!ParametersPath.Buffer)
{
TRACE_EVENT (GIT1);
Information = IO_ERR_GET_IF_TYPE_1;
Status = STATUS_UNSUCCESSFUL;
}
else
{
RtlZeroMemory(
ParametersPath.Buffer,
ParametersPath.MaximumLength
);
RtlAppendUnicodeStringToString(
&ParametersPath,
RegistryPath
);
RtlAppendUnicodeToString(
&ParametersPath,
L"\\EisaAdapter\\0"
);
/*************************************************************************/
/* Attempt to open the key - if we can this is Eisa bus. */
/*************************************************************************/
InitializeObjectAttributes(
&ParametersAttributes,
&ParametersPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
if (NT_SUCCESS(ZwOpenKey(
&ParametersKey,
MAXIMUM_ALLOWED,
&ParametersAttributes
)))
{
InterfaceType = Eisa;
TRACE_EVENT (Eisa);
DEBUG_PRINT (("IBMSYNC: Bus type is Eisa\n"));
TRACE_EVENT(GIT]);
ExFreePool(ParametersPath.Buffer);
return(TRUE);
}
}
if (NT_SUCCESS(Status))
{
/*************************************************************************/
/* Reset the Registry path to check for Identifier string in key */
/* \MultifunctionAdapter\0. */
/*************************************************************************/
RtlZeroMemory(
ParametersPath.Buffer,
ParametersPath.MaximumLength
);
ParametersPath.Length = 0;
RtlAppendUnicodeStringToString(
&ParametersPath,
RegistryPath
);
RtlAppendUnicodeToString(
&ParametersPath,
L"\\MultifunctionAdapter\\0"
);
/*************************************************************************/
/* Allocate the Rtl query table. */
/*************************************************************************/
Parameters = ExAllocatePool(
PagedPool,
sizeof(RTL_QUERY_REGISTRY_TABLE) * 2
);
if (!Parameters)
{
TRACE_EVENT (GIT2);
Information = IO_ERR_GET_IF_TYPE_2;
Status = STATUS_UNSUCCESSFUL;
}
else
{
/***********************************************************************/
/* Allocate memory to except Identifier type. */
/***********************************************************************/
RtlInitUnicodeString (&Identifier, NULL);
Identifier.MaximumLength = 20; /* Room for "EISA", "MCA" or "ISA" */
Identifier.Buffer = ExAllocatePool(
PagedPool,
Identifier.MaximumLength
);
if (!Identifier.Buffer)
{
TRACE_EVENT (GIT3);
Information = IO_ERR_GET_IF_TYPE_3;
Status = STATUS_UNSUCCESSFUL;
}
else
{
/*********************************************************************/
/* OK now finally pick up the Bus Type (Identifier) string from the */
/* Registry. */
/*********************************************************************/
RtlZeroMemory(
Parameters,
sizeof(RTL_QUERY_REGISTRY_TABLE) * 2
);
Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parameters[0].Name = L"Identifier";
Parameters[0].EntryContext = &Identifier;
Parameters[0].DefaultType = REG_SZ;
Parameters[0].DefaultData = L"None";
Parameters[0].DefaultLength = 8;
if (!NT_SUCCESS(RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
ParametersPath.Buffer,
Parameters,
NULL,
NULL
)))
{
TRACE_EVENT (GIT4);
Information = IO_ERR_GET_IF_TYPE_4;
Status = STATUS_UNSUCCESSFUL;
}
else
{
RtlInitUnicodeString (&MCAString, L"MCA");
if (!RtlCompareUnicodeString(&MCAString, &Identifier, TRUE))
{
InterfaceType = MicroChannel;
TRACE_EVENT (MCA );
DEBUG_PRINT (("IBMSYNC: Bus type is MicroChannel\n"));
}
else
{
InterfaceType = Isa;
TRACE_EVENT (ISA );
DEBUG_PRINT (("IBMSYNC: Bus type is Isa\n"));
}
}
}
}
}
/***************************************************************************/
/* Free any allocated memory before returning. */
/***************************************************************************/
if (ParametersPath.Buffer)
ExFreePool(ParametersPath.Buffer);
if (Identifier.Buffer)
ExFreePool(Identifier.Buffer);
if (Parameters)
ExFreePool(Parameters);
/***************************************************************************/
/* OK, so which Device Object do we use here? The driver can have */
/* multiple Device Objects set up; also the reading of the bus type from */
/* the Registry is done just once (i.e not on a per-Device basis). */
/* */
/* We use the last one hanging from the list - to get to this stage we */
/* must have at least one. */
/***************************************************************************/
if (!NT_SUCCESS(Status))
{
LogDriverError( pDriverObject->DeviceObject,
Status,
Information,
0L,
0L );
}
TRACE_EVENT(GIT]);
return(Status == STATUS_SUCCESS);
}
/**PROC+**********************************************************************/
/* */
/* Name GetDriverSpec ( */
/* CHAR *DriverName, */
/* PCONFIGDATA *pConfigData */
/* ); */
/* */
/* Purpose Hide the vagaries of initialising */
/* */
/* Params OUT DriverName: e.g. COM$DL01, null-terminated, 8+1 chars */
/* OUT FlavourName: e.g. SDLC or MPAA1, null-terminated, 5+1 chars */
/* OUT pConfigData: the config data record to be set up */
/* (a pointer is passed, not the structure itself) */
/* */
/* Return Value BOOLean: TRUE if there was another driver spec to get */
/* */
/* Operation At a guess, this will */
/* - enumerate all SDLC device drivers in CFG Reg. */
/* (RegEnumKeyEx(Key:Master\Software\CS\Drivers) */
/* or CS\SDLCDrivers? */
/* - if necessary, check the driver is an SDLC one */
/* - read config info to extract device name and type */
/* - get other config data into ConfigData */
/* */
/* Notes Uses static variables to indicate position in config; so */
/* no indication from user that this is first time is required. */
/* */
/* Called only during DriverEntry. */
/* */
/* The fact that a driver/flavour is returned does not imply */
/* the device is usable - this must be checked by calling */
/* DeviceInit. */
/* */
/**PROC-**********************************************************************/
BOOLean GetDriverSpec (CHAR *DeviceName,
PCONFIGDATA *pConfigData
)
{
static int DeviceNumber = 0;
/* very temporary - until we find out how to read config registry. */
if (++DeviceNumber > AT_COUNT)
return (FALSE);
strcpy (DeviceName, "\\Device\\COMDL$00");
DeviceName[strlen(DeviceName)-1] = ((char) ('0' + DeviceNumber));
*pConfigData = &ConfigData[DeviceNumber-1];
return(TRUE);
}
/**PROC+**********************************************************************/
/* */
/* Name GetV24Input ( */
/* PDX pDX */
/* ); */
/* */
/* Purpose Interpret pin inputs as presented by SDLC cards and stuff */
/* into IR fields */
/* */
/* Params IN pDX - the device extension */
/* */
/* Return Value None */
/* */
/**PROC-**********************************************************************/
void GetV24Input (PDX pDX)
{
UCHAR o;
TRACE_EVENT (<GV2);
pDX->pIR->V24In = 0; /* reset IR value. */
/***************************************************************************/
/* Reset the 'modem status changed' bit because after we read this */
/* hasn't changed */
/***************************************************************************/
WR_N_DELAY (pDX->ADAPTERBASE+AR_8255B,
IO_IN (pDX->ADAPTERBASE+AR_8255B) & CAST(~8,UCHAR));
/* 8 = modem status changed */
/***************************************************************************/
/* Read the 8255 Port C settings to get the current state of Test */
/***************************************************************************/
if (BITSOFF(IO_IN (pDX->ADAPTERBASE+AR_8255C), 0x40))
{
pDX->pIR->V24In |= IR_IV24Test; /* &40 = 0 => Test is Active */
}
/***************************************************************************/
/* Read the 8255 Port A settings. */
/***************************************************************************/
o = (IO_IN (pDX->ADAPTERBASE+AR_8255A));
if (!(o&1)) /* &1=0 implies RI on at interface */
{
pDX->pIR->V24In |= IR_IV24RI;
}
if (!(o&2)) /* &2=0 implies DCD on at interface */
{
pDX->pIR->V24In |= IR_IV24DCD;
}
if (!(o&8)) /* &8=0 implies CTS on at interface */
{
pDX->pIR->V24In |= IR_IV24CTS;
}
/***************************************************************************/
/* Used to do the 8273 Port A Reading to get hold of DSR here. There are */
/* slight problems with doing that */
/* - the 8273 can get upset if given three commands at once (e.g Rx, Tx */
/* AND CmdStringReadPortA) (this is surmountable) */
/***************************************************************************/
if (pDX->RxFSMCurState NE RxFSMStateReady || /* not receiving ... or */
pDX->TxFSMCurState EQ TxFSMStateIdle) /* transmitter idle */
{
pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA);
}
if (pDX->LastPortA & 4) /* is the DSR bit set??? */
{
pDX->pIR->V24In |= IR_IV24DSR;
}
TRACE_EVENT (GV2>);
}
/*****************************************************************************/
/* */
/* Name InitialiseAdapter */
/* */
/* Purpose Initialises and checks the hardware for a link. */
/* */
/* Params IN pDX, plus Synchronised, plus interrupt routines set up */
/* */
/* Return Value False on failure, plus SynchInformation set */
/* */
/*****************************************************************************/
BOOLean InitialiseAdapter (PDX pDX)
{
BOOLean rc;
TRACE_EVENT (<InA);
/***************************************************************************/
/* Resetting the adapter and check that it accepts commands. Then call */
/* the link options handler to set it up in the default mode. */
/***************************************************************************/
AdapterReset(pDX);
/***************************************************************************/
/* The adapter is now primed and ready to go. Set up a default link */
/* characteristics configuration and call link set-up. The link set-up */
/* routine SetLinkConfig returns carry set if problems occur. */
/***************************************************************************/
pDX->LinkMaxFrameSize = DEFAULT_FRAME_SIZE;
pDX->LinkOptionsByte = 0;
if (SetLinkConfig(pDX))
{
SetV24Output (pDX);
GetV24Input (pDX);
rc = TRUE; /* OK return code */
}
else
{
SynchTerminateAdapter(pDX);
pDX->Information = IO_ERR_HARDWARE_INIT_FAILURE;
rc = FALSE;
TRACE_EVENT (HwIF);
TRACE_RC(rc);
}
TRACE_EVENT (InA>);
return (rc);
}
/*****************************************************************************/
/* */
/* Name IoctlAbortReceiver */
/* */
/* Purpose Abort Receiver IOCtl Processor */
/* */
/* Params IN pDX */
/* */
/* OUT Requested function is processed. No return code */
/* */
/*****************************************************************************/
void IoctlAbortReceiver (PDX pDX)
{
TRACE_EVENT ([IAR);
RxFSMEvent (pDX, RxFSMInputStop); /* terminate with extreme prejudice */
/***************************************************************************/
/* Then reset the buffer pointers, discarding all the data in the receive */
/* buffer. */
/***************************************************************************/
RCVINFO_INIT(pDX);
TRACE_EVENT (IAR]);
}
/*****************************************************************************/
/* */
/* Name IoctlAbortTransmitter */
/* */
/* Purpose Abort Transmitter IOCtl Processor */
/* */
/* Params IN pDX */
/* */
/* OUT None */
/* */
/*****************************************************************************/
void IoctlAbortTransmitter (PDX pDX)
{
TRACE_EVENT ([IAT);
/***************************************************************************/
/* First stop the transmitter (if it is running). */
/***************************************************************************/
TxFSMEvent (pDX, TxFSMInputStop);
/*0025****************************************************************/
/*0025* Clear the underrun count. */
/*0025****************************************************************/
pDX->TxConsecutiveUnderrunCount = 0;
/***************************************************************************/
/* Then reset the transmit buffer pointers, discarding all the data in the */
/* buffer. */
/***************************************************************************/
pDX->TxNextToTransmit = 0;
pDX->TxNextToBuffer = 0;
/*0025**********************************************************************/
/*0025* Set up the maximum buffer size and the interface record */
/*0025* 'initialisation' buffer size. */
/*0025**********************************************************************/
pDX->TxStartUnusedArea = SENDBUF_SIZE;
pDX->pIR->TxMaxFrSizeNow = INIT_MAXFRSIZENOW;
XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow);
TRACE_EVENT (IAT]);
}
/*****************************************************************************/
/* */
/* Name IoctlSetInterfaceRecord */
/* */
/* Purpose / *IRMdl?* / Obsolete */
/* Set Interface Record Address handler */
/* */
/* Params IN pDX */
/* Implicit: the input buffer address (IrpSp->InputBufferLength */
/* + Irp->AssociatedIrp->SystemBuffer) describes the */
/* interface record */
/* */
/* OUT None */
/* */
/* Side Effect pDX->IoctlRetStatus & Information */
/* */
/* Return Value void */
/* */
/*****************************************************************************/
//void IoctlSetInterfaceRecord (PDX pDX)
//{
// PIRP pIrp = pDX->IoctlCurrentIrp;
// PIO_STACK_LOCATION
// pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
//
// TRACE_EVENT ([IoI);
//
// /***************************************************************************/
// /* The old story: allocate an MDL, probe and lock, map to system address */
// /* space and save the resulting system address in pIR. */
// /* */
// /* Free up Mdl and unlock pages if this is a subsequent call. */
// /***************************************************************************/
//
// if (pDX->pIRMdl != NULL)
// {
// /*************************************************************************/
// /* Protect ourselves from bozo users calling IoctlSetInterfaceRecrd twice*/
// /*************************************************************************/
// ASSERT (pDX->pIR != NULL);
// MmUnmapLockedPages (pDX->pIR, pDX->pIRMdl); /*IRMdl?*/
// IoFreeMdl (pDX->pIRMdl);
// }
//
// pDX->pIRMdl = IoAllocateMdl(pIrp->AssociatedIrp.SystemBuffer,
// pIrpSp->IRS_INLEN,
// sizeof (IRP),
// FALSE, /* this is not a secondary buffer */
// FALSE, /* no charge to quota */
// (PIRP)NULL/* not attached to IRP */
// );
//
// if (pDX->pIRMdl NE NULL) /* We could do the mapping */
// {
// MmProbeAndLockPages (pDX->pIRMdl, KernelMode, IoModifyAccess);
//
// pDX->pIR = MmMapLockedPages(pDX->pIRMdl, KernelMode);
// pDX->pIR->RxFrameCount = 0;
// pDX->pIR->TxMaxFrSizeNow = INIT_MAXFRSIZENOW;
// pDX->pIR->StatusCount = 0;
// pDX->pIR->V24In = 0;
// pDX->pIR->V24Out = 0;
// RtlZeroMemory (pDX->pIR->StatusArray, sizeof (pDX->pIR->StatusArray));
// TRACE_EVENT (pIR_);
// TRACE_DWORD (pDX->pIR);
// XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow);
// }
// else
// {
// pDX->pIR = NULL;
// pDX->Information = INFO_CANT_ALLOCATE_MDL;
// pDX->IoctlRetStatus = STATUS_INSUFFICIENT_RESOURCES;
// }
// TRACE_EVENT (IoI]);
//}
/*****************************************************************************/
/* */
/* Name IoctlSetLinkConfig */
/* */
/* Purpose Set Link Characteristics IOCtl Processor */
/* */
/* Sets the link configuration on the basis of the supplied */
/* parameters. This routine also aborts any outstanding */
/* activity in case there is a conflict between old and new */
/* parameters (switching to DMA for instance!). */
/* */
/* Params IN pDX */
/* */
/* OUT */
/* */
/* Side Effect pDX->IoctlRetStatus & Information */
/* */
/*****************************************************************************/
BOOLean IoctlSetLinkConfig (PDX pDX)
{
PIRP pIrp = pDX->IoctlCurrentIrp;
PIO_STACK_LOCATION
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
SLPARMS *pParms = pIrp->AssociatedIrp.SystemBuffer;
UCHAR NewOpt = pParms->SLLinkOptionsByte; /* easier to type */
BOOLean rc = FALSE; /* assume the worst! */
TRACE_EVENT ([IoC);
/***************************************************************************/
/* Issue the abort calls to get Tx and Rx synchronised with this. */
/***************************************************************************/
IoctlAbortReceiver (pDX);
IoctlAbortTransmitter(pDX);
/***************************************************************************/
/* Read the parameter data. Check that frame will fit the buffer. */
/***************************************************************************/
if (pIrpSp->IRS_INLEN NE sizeof(SLPARMS))
{
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_LINKCHAR_BUF_WRONG_SIZE;
}
else
if (pParms->SLFrameSize < 267) /* don't really want < 2+6+3+256 bytes!? */
{
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_FRAME_BUF_TOO_SMALL;
}
else
if (pParms->SLFrameSize > 2048) /* really SDLC frame bigger than 2Kb? */
{
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_FRAME_BUF_TOO_BIG;
}
else
if (pParms->SLLinkOptionsByte & LinkOption_InternalClock)
{
/*************************************************************************/
/* We have been asked to supply internal clocks (not supported by these */
/* adapters). */
/*************************************************************************/
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_NO_CLOCKS;
}
else
{
pDX->LinkMaxFrameSize = pParms->SLFrameSize;
/*************************************************************************/
/* Pick up the addresses and put them in the LCB. Then set General or */
/* specific receive depending on whether the addresses are zero. */
/*************************************************************************/
pDX->OurAddress1 = pParms->SLOurAddress1;
pDX->OurAddress2 = pParms->SLOurAddress2;
pDX->CmdStringReceive[4] = pParms->SLOurAddress1;
pDX->CmdStringReceive[5] = pParms->SLOurAddress2;
if (pParms->SLOurAddress1 EQ 0 &&
pParms->SLOurAddress2 EQ 0)
{
/***********************************************************************/
/* if both addresses are zero, we are SDLC primary and can accept */
/* frames targetted at any address */
/***********************************************************************/
pDX->CmdStringReceive[0] = 0xC0; /* set 3-byte General Rx command */
pDX->CmdStringReceive[1] = 2;
}
else
{
pDX->CmdStringReceive[0] = 0xC1; /* set 5-byte Specific Rx command */
pDX->CmdStringReceive[1] = 4;
}
/*************************************************************************/
/* Next, pick up the Link Options and make any necessary changes. */
/*************************************************************************/
if (NewOpt & LinkOption_FullDuplex /* turn off DMA if the link will */
&& NewOpt & LinkOption_DMA) /* be full duplex. */
{
/***********************************************************************/
/* Check whether the caller asked for DMA, but we couldn't support it */
/* - return warning error if so. */
/***********************************************************************/
NewOpt &= ~LinkOption_DMA;
pDX->Information = IO_ERR_NO_DMA_FDX; /* but r/c is OK. */
}
pDX->LinkOptionsByte = NewOpt; /* set the new options byte */
/*************************************************************************/
/* Clear the statistics data on the IF record if the appl wants to. */
/*************************************************************************/
if (NewOpt & LinkOption_ResetStatistics)
{ /* clear the statistics ?? */
RtlZeroMemory (pDX->pIR->StatusArray, sizeof(pDX->pIR->StatusArray));
pDX->pIR->StatusCount = 0;
}
/*************************************************************************/
/* Now set the options and start the receiver. */
/*************************************************************************/
if (SetLinkConfig (pDX)) /* call hardware to set options */
{
RxFSMEvent (pDX, RxFSMInputStart);/* start receiver if possible */
rc = TRUE; /* set good return code */
}
else
{
pDX->IoctlRetStatus = STATUS_DATA_ERROR;
pDX->Information = IO_ERR_HARDWARE_CMD_TIMEOUT_4;
}
}
TRACE_EVENT (IoC]);
return(rc);
}
/**PROC+**********************************************************************/
/* */
/* Name IoctlRxFrame */
/* */
/* Purpose Receive Request processor */
/* */
/* Copies next available frame to the application buffer. */
/* */
/* Params IN IRP: buffer length in Stack->OutputBufferLength (IRS_OUTLEN)*/
/* buffer pointer in UserBuffer */
/* */
/* OUT Available data, if any */
/* */
/* Side Effect pDX->IoctlRetStatus & Information */
/* */
/* Return Value TRUE - ignored */
/* */
/**PROC-**********************************************************************/
BOOLean IoctlRxFrame (PDX pDX)
{
PIRP pIrp = pDX->IoctlCurrentIrp;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
RFD *pRFD;
short RxLength;
UCHAR * GiveMeACharPtr;
TRACE_EVENT ([IoR);
/***************************************************************************/
/* Check to see if there are any frames waiting in the buffer. The ISR */
/* fills the buffer by incrementing the 'head' pointer but stops before it */
/* catches up with the 'tail'. */
/***************************************************************************/
if (RFD_CAN_GET(pDX)) /* then there is data present */
{
pRFD = &pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNextToGet];
/*************************************************************************/
/* We need to copy the data from our buffer into the user's buffer. */
/* The actual output length goes into the Information field */
/*************************************************************************/
RxLength = pRFD->RcvdDataLength;
if (CAST(RxLength, ULONG) + 2 > pIrpSp->IRS_OUTLEN)
{
/***********************************************************************/
/* Output buffer not as big as A + C + Data received */
/***********************************************************************/
pDX->IoctlRetStatus = STATUS_BUFFER_TOO_SMALL;
}
else
{
ASSERT (RxLength >= 0); /* n.b. but length = 0 is OK */
/* (RR's will have length of 0 in */
/* buffer, which we report to user */
/* as length of 0 */
if (RxLength >= 0) /* != -1 => really something there */
{
GiveMeACharPtr = MmMapLockedPages(pIrp->MdlAddress, KernelMode);
GiveMeACharPtr[0] = pRFD->SDLCAddressByte;
GiveMeACharPtr[1] = pRFD->SDLCControlByte;
RtlMoveMemory (&GiveMeACharPtr[2], pRFD->StartAddr, RxLength);
}
}
/*************************************************************************/
/* We signal the actual received length back in the IoStatusBlock, via */
/* pDX. */
/*************************************************************************/
pDX->Information = RxLength+2;
RFD_GOT(pDX); /* release the buffer we got */
/*************************************************************************/
/* Lastly decrement the count of received frames available in the shared */
/* data area. */
/*************************************************************************/
pDX->pIR->RxFrameCount--;
}
else
{
pDX->Information = 0;
TRACE_EVENT (IoRN);
}
TRACE_EVENT (IoR]);
return (TRUE);
}
/*****************************************************************************/
/* */
/* Name IoctlSetV24Output */
/* */
/* Purpose Handles the IoCtl to set the V24 Output */
/* */
/* Params IN pDX */
/* Implicit input: V24Out byte in IR is what is to be pumped out*/
/* */
/* OUT TRUE */
/* */
/* Side Effect pDX->IoctlRetStatus & Information */
/* */
/*****************************************************************************/
BOOLean IoctlSetV24Output (PDX pDX)
{
TRACE_EVENT ([IVO);
// temporary hack !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
pDX->pIR->V24Out = *(CAST (pDX->IoctlCurrentIrp->UserBuffer, UCHAR *));
// temporary hack !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (!SetV24Output(pDX)) /* call hardware processor */
{
return (FALSE);
}
TRACE_EVENT (IVO]);
return (TRUE);
}
/*****************************************************************************/
/* */
/* Name IoctlTxFrame */
/* */
/* Purpose Transmit frame request handler. Puts frames into the Tx */
/* buffer ready for the Tx FSM to use. */
/* */
/* Transmit Request Processor. */
/* */
/* Params IN pIrp parameters point to request packet data and length */
/* */
/* OUT Data transferred to Tx buffer ready to send. */
/* Transmitter FSM kicked. */
/* */
/* Side Effect pDX->IoctlRetStatus & Information */
/* */
/*****************************************************************************/
BOOLean IoctlTxFrame (PDX pDX)
{
ULONG BufferSpaceNeeded;
ULONG BufferSpaceAvailable;
PIRP pIrp = pDX->IoctlCurrentIrp;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
BOOLean rc = TRUE; /* normally OK */
TRACE_EVENT ([IoT);
BufferSpaceNeeded = pIrpSp->IRS_OUTLEN
+ 2; /* allow for frame length header */
XTRACE_ACTION (Tn, BufferSpaceNeeded);
XTRACE_ACTION (Tx, pDX->LinkMaxFrameSize);
XTRACE_ACTION (Tb, pDX->TxNextToBuffer);
XTRACE_ACTION (Tt, pDX->TxNextToTransmit);
if (CAST(pIrpSp->IRS_OUTLEN, int) < 2)
{
/*************************************************************************/
/* The minimum frame size is 2 */
/*************************************************************************/
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_TX_FRAME_TOO_SMALL;
rc = FALSE;
TRACE_RC(rc);
return(rc);
}
if (CAST(pIrpSp->IRS_OUTLEN, int) > pDX->LinkMaxFrameSize)
{
/*************************************************************************/
/* The frame is bigger than the maximum specified */
/*************************************************************************/
pDX->IoctlRetStatus = STATUS_INVALID_PARAMETER;
pDX->Information = IO_ERR_TX_FRAME_TOO_BIG;
rc = FALSE;
TRACE_RC(rc);
return(rc);
}
/***************************************************************************/
/* Check to see if there is room for the data in the transmit buffer. */
/* This code fills the buffer by incrementing the 'ToTransmit' pointer but */
/* stops before it catches up with the 'tail'. The ISR takes frames from */
/* the tail pointer, incrementing it until it matches ToTransmit. */
/***************************************************************************/
/***************************************************************************/
/* The ToBuffer and ToTransmit pointers could be anywhere. This code */
/* checks for ToTransmit < ToBuffer and works out the difference -> AX. */
/* If the ToTransmit is greater than the ToBuffer, there could be space at */
/* either end if the buffer - the top end is checked and, if too small, */
/* the ToTransmit is moved round to the start and then checked as */
/* ToTransmit < ToBuffer. */
/***************************************************************************/
if (pDX->TxNextToBuffer >= pDX->TxNextToTransmit)
{ /* ToTransmit is in front of ToBuffer*/
/*************************************************************************/
/* The free space is at the end of the buffer. If this is too small, */
/* set the 'end' for the ISR to the current ToTransmit and then move */
/* ToTransmit round to the front. Note: we don't go up to SENDBUF_SIZE */
/* because the TxStartUnusedArea must start on that */
/*************************************************************************/
BufferSpaceAvailable = SENDBUF_SIZE - 1 - pDX->TxNextToBuffer;
if (BufferSpaceNeeded > BufferSpaceAvailable)
/* not enough space at the end ? */
{
pDX->TxStartUnusedArea = pDX->TxNextToBuffer;
/* ISR will wrap at this point */
pDX->TxNextToBuffer = 0;
BufferSpaceAvailable = pDX->TxNextToTransmit-1;/* free up to bfr area*/
XTRACE_ACTION (Tu, pDX->TxStartUnusedArea);
XTRACE_ACTION (Tb, pDX->TxNextToBuffer);
XTRACE_ACTION (Ta, BufferSpaceAvailable);
}
}
else
{
/*************************************************************************/
/* the free space is from NextToTransmit up to NextToBuffer */
/*************************************************************************/
BufferSpaceAvailable = pDX->TxNextToBuffer - 1 - pDX->TxNextToTransmit;
XTRACE_ACTION (Ta, BufferSpaceAvailable);
}
if (BufferSpaceAvailable >= BufferSpaceNeeded)
{
/*************************************************************************/
/* The 'Max Tx Frame is initialised to be a little less than half the */
/* total size of the transmit buffer and is decremented by half the size */
/* of each frame put in (rounded UP). It is kept at half because the */
/* bfr can get split into two pieces and we need a contiguous area. */
/*************************************************************************/
pDX->pIR->TxMaxFrSizeNow -= (BufferSpaceNeeded + 1) >> 1;
XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow);
pDX->pSendBuf [pDX->TxNextToBuffer] = LOBYTE(BufferSpaceNeeded-2);
pDX->pSendBuf [pDX->TxNextToBuffer+1] = HIBYTE(BufferSpaceNeeded-2);
/* set length to data length */
/* (-2 => less length itself)*/
RtlMoveMemory (&(pDX->pSendBuf[pDX->TxNextToBuffer+2]),
pIrp->UserBuffer,
BufferSpaceNeeded-2);
/*************************************************************************/
/* Now set up the new buffer ToTransmit position. */
/*************************************************************************/
pDX->TxNextToBuffer += BufferSpaceNeeded;
XTRACE_ACTION (Tb, pDX->TxNextToBuffer);
ASSERT (pDX->TxNextToBuffer < SENDBUF_SIZE);
/*************************************************************************/
/* Kick the Transmit FSM to start it off. */
/*************************************************************************/
TxFSMEvent (pDX, TxFSMInputStart);
// and returned status is TRUE;
}
else
{
/*************************************************************************/
/* There is no room for the frame - return an error. */
/*************************************************************************/
pDX->IoctlRetStatus = STATUS_BUFFER_TOO_SMALL;
pDX->Information = IO_ERR_TX_BUFFER_FULL;
rc = FALSE;
TRACE_RC(rc);
}
TRACE_EVENT (IoT]);
return (rc);
}
/*****************************************************************************/
/* */
/* Name RxFSMActionInvalid */
/* */
/* Purpose Error input to FSM */
/* */
/* This routine should never be called as it implies an */
/* 'impossible state/input combination. This is taken to mean */
/* that adapter status has been incorrectly reported and is */
/* treated as a hardware error. */
/* */
/* Params IN pDX */
/* */
/* OUT Error processing started by RQ FSM input */
/* */
/*****************************************************************************/
void RxFSMActionInvalid (PDX pDX)
{
TRACE_EVENT (RAI:);
pDX->pIR->StatusArray[SA_HardwareError]++;
pDX->pIR->StatusCount ++;
pDX->DPCAction |= DPC_ACTION_PULSE;
TRACE_EVENT (RAI;);
}
/*****************************************************************************/
/* */
/* Name RxFSMActionRestart */
/* */
/* Purpose Coming back after having held rx so transmitter can go */
/* so - we can switch buffers so DMA can run for the whole buf */
/* */
/* Params IN pDX */
/* */
/* Implicit input: that when the receiver was stopped that the */
/* 'NowBeingPut' RFD for this adapter was allocated and init- */
/* ialised properly. So this is just a 'start reciver'. */
/* */
/* OUT Receiver started */
/* */
/* */
/*****************************************************************************/
void RxFSMActionRestart (PDX pDX)
{
RFD * pRFD = & pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNowBeingPut];
TRACE_EVENT (RRs:);
pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA);
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
StopDMA (pDX);
XASSERT (!pDX->DMAIsActive);
/*************************************************************************/
/* only worry about this if we using DMA and if other buffer not in use */
/*************************************************************************/
if (!(RCVBUF_INUSE(pRFD->BufPtr->OtherBuffer)))
{
/* use other buffer */
pRFD->BufPtr = pRFD->BufPtr->OtherBuffer;
pRFD->StartIndex= 0;
pRFD->StartAddr = &pRFD->BufPtr->Data[0];
}
}
RxFSMActionStart (pDX);
TRACE_EVENT (RRs;);
}
/*****************************************************************************/
/* */
/* Name RxFSMActionStart */
/* */
/* Purpose Starts the Receiver and DMA (if configured). */
/* */
/* Params IN pDX */
/* */
/* Implicit input: that when the receiver was stopped that the */
/* 'NowBeingPut' RFD for this adapter was allocated and init- */
/* ialised properly. So this is just a 'start reciver'. */
/* */
/* OUT Receiver started */
/* */
/* */
/*****************************************************************************/
void RxFSMActionStart (PDX pDX)
{
RFD * pRFD = & pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNowBeingPut];
int AvailableByteCount = RCVDATABUF_SIZE - 1 - pRFD->StartIndex;
/* allocate all the rest of the buffr*/
/* This really is the available bytes*/
USHORT CmdByteCount = CAST (AvailableByteCount - 2, USHORT);
/* number of bytes to command 8273 */
TRACE_EVENT (RSt:);
XASSERT (AvailableByteCount >= CAST (pDX->LinkMaxFrameSize, int));
/* frame must fit into rest of buffer*/
/* or previous code screwed up */
XTRACE_ACTION (Ra, AvailableByteCount);
/***************************************************************************/
/* Prime the DMA if necessary. */
/***************************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
if (!pDX->DMAIsActive)
{
/***********************************************************************/
/* If the DMA is already running we just leave it running. Otherwise */
/* need to start it */
/***********************************************************************/
PHYSICAL_ADDRESS L;
L = pRFD->BufPtr->DataPhysAddr;
L.LowPart += (ULONG) pRFD->StartIndex;
StartDMA (pDX,
L,
CmdByteCount, /* should be one less, but we've left*/
/* two bytes slop above anyway */
DMACmdWrite
);
}
XASSERT (pDX->DMAIsActive);
}
else
{
pDX->pRxPIOData = pRFD->StartAddr;
XTRACE_EVENT (RPIO);
XTRACE_DWORD (pDX->pRxPIOData);
}
pDX->CmdStringReceive[2] = LOBYTE(CmdByteCount);
pDX->CmdStringReceive[3] = HIBYTE(CmdByteCount);
Write8273Cmd (pDX, pDX->CmdStringReceive);
TRACE_EVENT (RSt;);
}
/****************************************************************************/
/* */
/* Name RxFSMActionStop */
/* */
/* Purpose Stops the Receiver and DMA (if configured). */
/* */
/* Params IN pDX */
/* */
/* OUT Receiver stopped */
/* */
/****************************************************************************/
void RxFSMActionStop (PDX pDX)
{
TRACE_EVENT (RSo:);
/*********************************************************************/
/* Check whether DMA is supported and kill the DMA Channel if so. */
/*********************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
StopDMA (pDX);
XASSERT (!pDX->DMAIsActive);
}
/*********************************************************************/
/* Issue a Disable command to the 8273. */
/*********************************************************************/
Write8273Cmd (pDX, pDX->CmdStringDisableReceiver);
/***************************************************************************/
/* Following IO_DELAY is an attempt to prevent a hang reading port A in */
/* this situation */
/***************************************************************************/
IO_DELAY(5L);
pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA);
TRACE_EVENT (RSo;);
}
/****************************************************************************/
/* */
/* Name RxFSMActionRcvError */
/* */
/* Purpose Updates the received data statistics and restarts the Rcvr */
/* */
/* Carries out end-of-frame processing on error results. */
/* */
/* Params IN BX -> error result from the 8273 */
/* */
/* OUT Updated Rx stats - receiver restarted. */
/* */
/****************************************************************************/
void RxFSMActionRcvError (PDX pDX)
{
/***************************************************************************/
/* Clear the top nibble of result so we can detect non-octet boundary errs */
/***************************************************************************/
UCHAR ResultCode = CAST(pDX->RxResultBuffer[0] & 0x0F, UCHAR);
static
USHORT ErrorMapArray[] = /* f: 8273code -> index in StatusArry*/
{
/* 0General */ SA_Spare + 256, /* +256->stop receiver */
/* 1Selective */ SA_Spare + 256,
/* 2<unused> */ SA_HardwareError, /* shouldn't be generated */
/* 3CRC */ SA_CRC_Error + 256,
/* 4Abort */ SA_RxAbort + 256,
/* 5Idle */ 0, /* don't use */
/* 6EOP */ SA_Spare,
/* 7Short */ SA_RxFrameTooShort + 256,
/* 8DMAOverrun */ SA_RxOverrun,
/* 9BufO */ SA_RxFrameTooBig,
/* 10RLSD */ SA_DCDDrop,
/* 11RxIntOverrun */ SA_HardwareError,
/* 12 tx */ SA_HardwareError,
/* 13 tx */ SA_HardwareError,
/* 14 tx */ SA_HardwareError,
/* 15 tx */ SA_HardwareError, /* 15 for 0x0F & value */
};
XASSERT (ResultCode < 12);
XTRACE_ACTION (Rm, ErrorMapArray[ResultCode]);
/***************************************************************************/
/* Filter out 'idle' indications before going through checks */
/***************************************************************************/
if (ResultCode NE ARxR_ErrIdle) /* no StopRequired - idle stop Rcvr */
{
/*************************************************************************/
/* Increment the statistics and set the flag to pulse event */
/*************************************************************************/
pDX->pIR->StatusArray[LOBYTE(ErrorMapArray[ResultCode])]++;
pDX->pIR->StatusCount++;
pDX->DPCAction |= DPC_ACTION_PULSE;
}
/*********************************************************************/
/* Check whether DMA is supported and kill the DMA Channel if so. */
/*********************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
StopDMA (pDX);
XASSERT (!pDX->DMAIsActive);
}
/***************************************************************************/
/* For those situations that need it (i.e. where the error has left the */
/* receiver active), stop the receiver. As a result, the receiver will */
/* always be stopped after an error. This is just being paranoid - */
/* shouldn't really need to. */
/***************************************************************************/
if (HIBYTE(ErrorMapArray[ResultCode])) /* high byte->stop required */
{
RxFSMActionStop (pDX);
}
/***************************************************************************/
/* Now call the action routine to restart the receiver. */
/***************************************************************************/
RxFSMActionStart (pDX);
}
/*****************************************************************************/
/* */
/* Name RxFSMActionRcvOK */
/* */
/* Purpose Updates the Receiver buffer data to reflect new data, does */
/* some statistics adjustment and kicks the Rx sema4. */
/* */
/* Carries out end-of-received frame processing. */
/* */
/* Params IN pDX */
/* Implicit:pDX->ResultBuffer has all results */
/* [0] = OK result code */
/* [1] = Length (lo) */
/* [2] = Length (hi) */
/* [3] = Address */
/* [4] = Control */
/* */
/* OUT Updated Rx buffer and stats - receiver restarted. */
/* */
/*****************************************************************************/
void RxFSMActionRcvOK (PDX pDX)
{
/* this is the 'Old' one */
/* New NowBeingPut */
BOOLean CanMoveOn = TRUE; /* won't be true if no more RFDs OR */
/* no space in this buffer and other */
/* buffer in use */
BOOLean MustStopReceiver = TRUE;
short NewNBP = CAST((pDX->RcvInfo.RFDNowBeingPut+1) % RFDARRAY_SIZE,
short);
RFD * pNewRFD= & pDX->RcvInfo.RFDArray[NewNBP];
USHORT RcvdLength;
RFD * pRFD = & pDX->RcvInfo.RFDArray[pDX->RcvInfo.RFDNowBeingPut];
TRACE_EVENT (ROk:);
XTRACE_OBJECT(Rp, (*pRFD));
XTRACE_DWORD (pRFD);
XTRACE_OBJECT(Rb, (*(pRFD->BufPtr)));
XTRACE_ACTION(Rs, pRFD->StartIndex);
/***************************************************************************/
/* First, copy results into existing RFD */
/***************************************************************************/
XASSERT (pRFD->RcvdDataLength EQ -1); /* this buffer hasn't been 'filled' */
pRFD->SDLCAddressByte = pDX->RxResultBuffer[3];
pRFD->SDLCControlByte = pDX->RxResultBuffer[4];
pRFD->RcvdDataLength = MAKEUSHORT (pDX->RxResultBuffer[1],
pDX->RxResultBuffer[2]);
RcvdLength = pRFD->RcvdDataLength;
XTRACE_ACTION (Rl,RcvdLength);
/***************************************************************************/
/* Double-check the length the 8273 tells us compared to what we rcvd on */
/* the interrupt side */
/***************************************************************************/
#ifdef XDEBUG
if (BITSOFF(pDX->GrabbedResources,GRABBEDRESOURCE_GOTDMA))
XASSERT (CAST(pRFD->RcvdDataLength, int) EQ
(pDX->pRxPIOData - pRFD->StartAddr));
#endif
/***************************************************************************/
/* Now figure out a) if we can fit next frame into this buffer, and b) */
/* failing that if we can start using the other buffer */
/***************************************************************************/
if (NewNBP EQ pDX->RcvInfo.RFDNextToGet)
{
CanMoveOn = FALSE; /* can't wrap round onto next to get */
}
else
{
/*************************************************************************/
/* Set up the new startindex - this is OK even if we don't move onto new */
/* RFD because it's not being used. */
/* */
/* Then see if we can let the receiver run - if not, we'll have to stop */
/* it and switch DMA etc. */
/* */
/* If we let the receiver run, we must calculate exactly where the data */
/* for the next frame is going to go! */
/* */
/* Note that if we are receiving into a given buffer, we always have the */
/* rest of that buffer to play with, because we only strt using a new */
/* buffer if all received frames that were in it have been got out. */
/*************************************************************************/
pNewRFD->StartIndex = CAST(pRFD->StartIndex + RcvdLength, short);
if (CAST(pNewRFD->StartIndex, int) +
CAST(pDX->LinkMaxFrameSize, int) +
2 /* +2 for possible CRC dribble */
< RCVDATABUF_SIZE) /* (and some more paranoia). */
{
/***********************************************************************/
/* next packet fits into this buffer - can let receiver run */
/***********************************************************************/
pNewRFD->BufPtr = pRFD->BufPtr;
pNewRFD->StartAddr = &pNewRFD->BufPtr->Data[pNewRFD->StartIndex];
MustStopReceiver = FALSE;
}
else
{
/***********************************************************************/
/* This buffer is hopeless, so how about the other one - have all it's */
/* frams been pulled? If so, we can use it */
/***********************************************************************/
if (RCVBUF_INUSE(pRFD->BufPtr->OtherBuffer))
{
CanMoveOn = FALSE; /* cant use him either */
}
else
{
/* use other buffer */
pNewRFD->BufPtr = pRFD->BufPtr->OtherBuffer;
pNewRFD->StartIndex= 0;
pNewRFD->StartAddr = &pNewRFD->BufPtr->Data[0];
}
}
}
/***************************************************************************/
/* If necessary, stop the receiver */
/***************************************************************************/
if (MustStopReceiver)
{
RxFSMActionStop (pDX);
}
/***************************************************************************/
/* If we will be moving on, do it & increment the count of waiting frames. */
/* Whatever happens, we must signal that RFD is empty via RcvdDataLength */
/***************************************************************************/
if (CanMoveOn)
{
RFD_PUT(pDX);
pDX->RcvInfo.RFDNowBeingPut = NewNBP;
pRFD = pNewRFD;
pDX->pIR->RxFrameCount++; /* show Rx frame available */
}
pRFD->RcvdDataLength = -1; /* empty ! */
TRACE_OBJECT(Rp, (*pRFD));
XTRACE_DWORD (pRFD);
XTRACE_OBJECT(Rb, (*(pRFD->BufPtr)));
XTRACE_ACTION(Rs, pRFD->StartIndex);
/***************************************************************************/
/* A frame is ready for the application (or the buffer is full and the */
/* application should do some receiving) - set the flag so that its */
/* semaphore will be cleared by the ISR when we return. */
/***************************************************************************/
pDX->DPCAction |= DPC_ACTION_PULSE;
/***************************************************************************/
/* Now call the action routine to restart the receiver. */
/***************************************************************************/
RxFSMActionStart (pDX);
TRACE_EVENT (ROk;);
}
/*****************************************************************************/
/* */
/* Name RxFSMEvent */
/* */
/* Purpose Interprets inputs from the Request handlers and from the */
/* ISR and calls the relevant action routines. */
/* */
/* There are two fundamental states: */
/* */
/* - idle */
/* - ready (i.e. receiver is active) */
/* */
/* Each of these states, however, can also exist in two states */
/* depending on the activity of the transmitter which has to */
/* restrain the receiver when it is using the DMA. These sub- */
/* states are idle(held) and ready(held). */
/* */
/* Purpose Routes processing to the relevant action routines. The FSM */
/* action must be synchronized. The action routines are allowed*/
/* to call back to the FSM. */
/* */
/* Params IN pDX */
/* FSMInput - the input value */
/* Implicit - pDX->RxResultBuffer result bytes pulled from */
/* Rx status register on 8273 */
/* */
/* OUT Action routine called and new state set. */
/* */
/*****************************************************************************/
void RxFSMEvent (PDX pDX, int Input)
{
RXFSMENTRY *e = &RxFSM [Input] [pDX->RxFSMCurState];
/***************************************************************************/
/* The FSM consists of a 'state' which is actually the address of the */
/* current FSM 'column'. The 'input' is an offset down the column at */
/* which is located an action routine address and a new state. */
/***************************************************************************/
TRACE_EVENT (<RFE);
ASSERT (pDX->RxFSMCurState < RXFSMSTATECOUNT);
ASSERT (Input < RXFSMINPUTCOUNT);
TRACE_EVENTNAME(RxFSMStateNames[pDX->RxFSMCurState]);
TRACE_EVENTNAME(RxFSMInputNames[Input]);
/***************************************************************************/
/* Reset the state to the new state specified in the FSM table */
/* (Important to do this before the actions because some of them then do */
/* recursive calls to FSMEvent */
/***************************************************************************/
pDX->RxFSMCurState = e->NewState;
ASSERT (e->NewState < RXFSMSTATECOUNT);
TRACE_EVENTNAME(RxFSMStateNames[pDX->RxFSMCurState]);
/***************************************************************************/
/* Do the action specified by the FSM table entry */
/***************************************************************************/
(*(e->pRxActionRoutine)) (pDX);
TRACE_EVENT (RFE>);
}
/*****************************************************************************/
/* */
/* Name FSMNullAction */
/* */
/* Purpose Carries out a 'nul' action for both Tx and Rx FSM */
/* */
/*****************************************************************************/
void FSMNullAction (PDX pDX)
{
UNREFERENCED_PARAMETER (pDX);
return;
}
/**PROC+**********************************************************************/
/* */
/* Name SetLinkConfig (pDX) */
/* */
/* Purpose Link Configuration Characteristics set-up. */
/* Primes the 8273 and V24 interface in the manner required */
/* required by a particular set of link configuration options. */
/* */
/* Params IN Link Options byte (pDX->LinkOptions) set up */
/* Implicit input: the adapter has been reset and is ready to go*/
/* */
/* OUT Hardware is set up as indicated or carry set. */
/* */
/* Return Value BOOLean: TRUE if options set up OK, otherwise FALSE */
/* */
/**PROC-**********************************************************************/
BOOLean SetLinkConfig (PDX pDX)
{
UCHAR o;
BOOLean rc;
TRACE_EVENT (<SLC);
ASSERT (pDX->HardwareError EQ FALSE); /* Reset function should turn off */
/* hardware error flag */
/***************************************************************************/
/* Check the configuration bits and use the data to set up the mode set */
/* and reset commands held on the LCB. Then run through the whole batch */
/* in a tight loop at the end. */
/***************************************************************************/
/***************************************************************************/
/* Operating mode */
/***************************************************************************/
o = AP_OMBFR; /* assume default (buffered) */
if (pDX->LinkOptionsByte & LinkOption_HDLC)
{
o |= AP_OMX25; /* set HDLC Abort */
}
pDX->CmdStringSetOpMode[2] = CAST(o, UCHAR);
pDX->CmdStringResetOpMode[2]= CAST(o | APR_OM, UCHAR);
/***************************************************************************/
/* Serial IO Mode */
/***************************************************************************/
o = 0; /* assume SIO mode is NRZ */
if (pDX->LinkOptionsByte & LinkOption_NRZI) /* then check for NRZI selected */
{
o |= AP_IONZI; /* set NRZI bit in the mask */
}
pDX->CmdStringSetSerialIOMode[2]= CAST (o, UCHAR);
pDX->CmdStringResetSerialIOMode[2]= CAST (o | APR_IO, UCHAR);
/* set the unused bits for reset */
/***************************************************************************/
/* Sort out DMA. Assume for now we will be able to use an architectural */
/* NT-defined way of avoiding conflicts on DMA channels, */
/***************************************************************************/
pDX->CmdStringDataTransferMode[0]= 0x97;/* assume Data Transfer is PIO */
pDX->CmdStringDataTransferMode[2]= 1;
pDX->GrabbedResources &= ~GRABBEDRESOURCE_GOTDMA; /*and clear DMA privelege*/
if (pDX->LinkOptionsByte & LinkOption_DMA && /* DMA Requested ? */
pDX->ConfigData.DMAChannel NE 0) /* and DMA possible for card */
{
/*************************************************************************/
/* Read this link's configured DMA channel and check for non-0 */
/*************************************************************************/
///////////////////////////////////////////////////////////////////////////////
// guff to do with protecting ourselves from other guy using same DMA channel
// reinstate later if necessary
//
// if <SI eq <offset SELCBL1>>
//
// mov DI,offset SELCBL2
//
// else
//
// mov DI,offset SELCBL1
//
// }
//
// /**********************************************************************/
// /* Check whether the links use the same DMA and grab it if not. */
// /* Otherwise look to see whether the other link does not currently */
// /* have DMA. */
// /**********************************************************************/
//
// if <AL ne pDX->DMAChan> OR
// test pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA
// if z
///////////////////////////////////////////////////////////////////////////////
pDX->GrabbedResources |= GRABBEDRESOURCE_GOTDMA; /* register ownership! */
pDX->DMAIsActive = FALSE;
pDX->CmdStringDataTransferMode[0]= 0x57; /* and set DT mode to DMA */
pDX->CmdStringDataTransferMode[2]= 0xFE; /* ..rather than interrupts */
/*************************************************************************/
/* If this is MCA Extended DMA then we have to set up an I/O transfer */
/* address. */
/*************************************************************************/
if (pDX->ConfigData.DMAChannel NE StandardDMAChannel)
{
///////////////////////////////////////////////////////////////////////////////
//?ML?
// from semdh.asm
// and AL,0fh ;clear top four bits
// out LDD_DMAEFR,AL ;write out the fncn cmnd
//
// mov AX,LCB.LCBPrtBase
// add AX,LDD_73Data
// out LDD_DMAEFE,AL ;write out the LOB
// SEPDHDLY ;let it take effect
// mov AL,AH
// out LDD_DMAEFE,AL ;write out the HOB
//////////////////////////////////////////////////////////////////////////////
USHORT Addr = CAST(pDX->ADAPTERBASE+AR_8273D, USHORT);
WR_N_DELAY (DMAExtdFnRegister, CAST(pDX->ConfigData.DMAChannel & 0x0F, \
UCHAR));
WR_N_DELAY (DMAExtdFnRegister, LOBYTE(Addr)); /* write out the LOB */
WR_N_DELAY (DMAExtdFnRegister, HIBYTE(Addr)); /* and HOB */
}
}
/***************************************************************************/
/* Now actually issue the mode setting commands to the 8273. */
/***************************************************************************/
Write8273Cmd(pDX,pDX->CmdStringResetOpMode);
Write8273Cmd(pDX,pDX->CmdStringResetSerialIOMode);
Write8273Cmd(pDX,pDX->CmdStringSetOpMode);
Write8273Cmd(pDX,pDX->CmdStringSetSerialIOMode);
Write8273Cmd(pDX,pDX->CmdStringDataTransferMode);
rc = !pDX->HardwareError;
TRACE_RCFALSE(rc);
TRACE_EVENT (SLC>);
return (rc);
}
/**PROC+**********************************************************************/
/* */
/* Name SetV24Output ( */
/* PDX pDX */
/* ); */
/* */
/* Purpose Primes the 8273 and V24 interfaces as requested in IR->V24Out*/
/* */
/* Params IN pDX - the device extension */
/* Implicit input: V24Out byte in IR is what is to be pumped out*/
/* */
/* Return Value BOOLean: TRUE if commands etc. correctly written */
/* */
/* Operation */
/* */
/**PROC-**********************************************************************/
BOOLean SetV24Output (PDX pDX)
{
/**********************************************************************/
/* Do the 8255 Port B settings first. Note that their sense is */
/* logically inverted and 0 = ON. */
/*********************************************************************/
UCHAR o;
UCHAR V24Out = pDX->pIR->V24Out;
o = 8; /* NOT reset modem status logic */
o |= (V24Out & IR_OV24DSRS) /* user wants DSRS on? */
? 0 /* 0 in port B turns on DSRS */
: 1;
o |= (V24Out & IR_OV24SlSt) /* user wants Select Standby on? */
? 0 /* 0 in port B turns on Select Stndby*/
: 2;
o |= (V24Out & IR_OV24Test) /* user wants Test on? */
? 0 /* 0 in port B turns on Test */
: 4;
WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, o);
/*********************************************************************/
/* Now do the 8273 Port B settings. */
/*********************************************************************/
o = 0;
o |= (V24Out & IR_OV24RTS) /* user wants RTS up? */
? 1
: 0;
o |= (V24Out & IR_OV24DTR) /* user wants DTR up? */
? 4
: 0;
pDX->CmdStringSetPortB[2]= o; /* save value in 'set' cmd */
pDX->CmdStringResetPortB[2]= CAST(0xC0 | o, UCHAR);
/* and also in 'reset' cmd */
Write8273Cmd (pDX,pDX->CmdStringSetPortB); /* turn on what should be on */
Write8273Cmd (pDX,pDX->CmdStringResetPortB); /*and turn off what shouldn't*/
return(!pDX->HardwareError);
}
/*****************************************************************************/
/* */
/* Name StartDMA ( */
/* PDX pDX */
/* PHYSICAL_ADDRESS PhysicalAddress */
/* USHORT BufferLength */
/* UCHAR OpCode */
/* ); */
/* */
/* Purpose Issues a Receive/Transmit command to the DMA. The address */
/* and length information are all in registers, other than the */
/* segment physical base address which is in global data. */
/* */
/* Params IN PhysicalAddress (not System VAS address!) */
/* BufferLength is maximum data length */
/* (although the 8273 controls exactly how many transfers are */
/* pulled). So this count really acts as a stopper and should */
/* (due to stupid design of 8273) be one less than overflow val */
/* OpCode is DMACmdRead/Write */
/* */
/* OUT Command issued to DMA. */
/* */
/* Modified: 31/03/88 Initial coding */
/* */
/*****************************************************************************/
void StartDMA(PDX pDX,
PHYSICAL_ADDRESS PhysicalAddress,
USHORT BufferLength,
UCHAR OpCode)
{
PHYSICAL_ADDRESS a;
USHORT b;
TRACE_EVENT (<SDM);
ASSERT (!DMACrosses64K(PhysicalAddress.LowPart, BufferLength));
/***************************************************************************/
/* First, test whether the channel number is set to 1. If not, then this */
/* is an MCA machine using a non-standard channel, so dothe MCA version of */
/* this routine */
/***************************************************************************/
if (pDX->ConfigData.DMAChannel EQ StandardDMAChannel)/* good ole channel one */
{
/*************************************************************************/
/* Mask off the channel we're about to play with. */
/*************************************************************************/
IO_OUT (DMAMaskRegister, DMAMaskChannel1);
/*************************************************************************/
/* The hard part is generating an address to give the DMA chip. We */
/* already know, because initialisation ensures it, that the buffer will */
/* not span a 64k boundary, but we have to generate the full physical */
/* address from the offset supplied in DI and the physical address of */
/* DGROUP read in during initialisation. */
/*************************************************************************/
IO_OUT (DMAFirstByteFlipFlop,0); /* data ignored, OUT resets FF */
a = PhysicalAddress;
WR_N_DELAY (DMAPhysAddress, a.LowPart & 0xFF); /* first, set physaddr low byte */
a.LowPart = a.LowPart >> 8;
WR_N_DELAY (DMAPhysAddress, a.LowPart & 0xFF); /* next, set physaddr high byte */
a.LowPart = a.LowPart >> 8;
WR_N_DELAY (DMAPageRegister,a.LowPart & 0xFF) /* finally 64k page register value*/
/*************************************************************************/
/* Next give the chip the maximum size count passed in BufferLength. */
/*************************************************************************/
WR_N_DELAY (DMAFirstByteFlipFlop,0); /* ML did this so I will too ? */
b = BufferLength;
WR_N_DELAY (DMACountRegister, a.LowPart & 0xFF);
b = b >> 8;
WR_N_DELAY (DMACountRegister, a.LowPart & 0xFF);
/*************************************************************************/
/* Set up the mode value as passed in. */
/*************************************************************************/
WR_N_DELAY (DMAModeRegister, OpCode);
/*************************************************************************/
/* Finally kick the chip off by clearing the channel mask bit. */
/*************************************************************************/
IO_OUT (DMAMaskRegister, DMAClearChannel1);
}
else
{
/*************************************************************************/
/* This must be an MCA machine with programmable DMA channels. The only */
/* actual option at present is '7' for the second MPA/A. */
/*************************************************************************/
ASSERT (pDX->ConfigData.DMAChannel EQ 7);
/*************************************************************************/
/* Set the mask for this channel. */
/*************************************************************************/
IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMASetMask);
/*************************************************************************/
/* Give the DMA registers the data xfer start address. */
/*************************************************************************/
IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMAAddress);
a = PhysicalAddress;
WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* and then set address bits 0-7 */
a.LowPart = a.LowPart >> 8;
WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* set address bits 8 - 15 */
a.LowPart = a.LowPart >> 8;
WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* set address bits 16 - 23 */
/*************************************************************************/
/* Now set up the maximum xfer count. */
/*************************************************************************/
IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMACount);
a.LowPart = BufferLength;
WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* length low byte */
a.LowPart = a.LowPart >> 8;
WR_N_DELAY (DMAExtdFnRegister, a.LowPart & 0xFF); /* length high byte */
/*************************************************************************/
/* Now set the mode register for the channel (read/write). This is done */
/* on the basis of the original old-style mode being for read or for */
/* write. */
/*************************************************************************/
IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMAMode);
WR_N_DELAY (DMAExtdFnRegister, (OpCode EQ DMACmdWrite) ? 0x0D : 0x05);
/*************************************************************************/
/* Last job is to reset the mask register for the channel. */
/*************************************************************************/
IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMAClearMask);
}
pDX->DMAIsActive = TRUE;
TRACE_EVENT (SDM>);
}
/*****************************************************************************/
/* */
/* Name StopDMA */
/* */
/* Purpose Link Device Driver Stop DMA Routine. */
/* */
/* Kills the DMA by masking the appropriate channel. */
/* */
/* Params IN pDX */
/* Implicit: DMA channel allocated */
/* */
/* OUT Command issued to DMA. */
/* */
/*****************************************************************************/
void StopDMA (PDX pDX)
{
TRACE_EVENT (<ZDM);
ASSERT (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA);
if (pDX->ConfigData.DMAChannel EQ StandardDMAChannel)
{
/*************************************************************************/
/* Channel #1 - set the mask directly. */
/*************************************************************************/
IO_OUT (DMAMaskRegister, DMAMaskChannel1);
}
else
{
/*************************************************************************/
/* Not channel 1 - set the mask using the Extended function addressing */
/* register. */
/*************************************************************************/
IO_OUT (DMAExtdFnRegister, pDX->ConfigData.DMAChannel | MCADMASetMask);
}
pDX->DMAIsActive = FALSE;
TRACE_EVENT (ZDM>);
}
/**PROC+**********************************************************************/
/* */
/* Name: SynchReset8273 ( */
/* PDEVICE_OBJECT pDeviceObject */
/* ) */
/* */
/* Purpose: Reset the 8273 and set the 'Closing' flag so that following */
/* interrupts are ignored (until we disable) */
/* with interrupt processing */
/* */
/* Params: IN Context is really pDX */
/* */
/* Return Value:None */
/* */
/* Operation: */
/* */
/* */
/**PROC-**********************************************************************/
BOOLEAN SynchReset8273 (PVOID Context)
{
PDX pDX = (PDX) Context;
TRACE_EVENT (<XR8);
pDX->AdapterIsClosing = TRUE;
AdapterReset (pDX);
TRACE_EVENT(XR8>);
return (FALSE);
}
/**PROC+**********************************************************************/
/* */
/* Name: SynchTerminateAdapter( */
/* PDEVICE_OBJECT pDeviceObject */
/* ) */
/* */
/* Purpose: Close a particular device - stuff that needs to be interlockd*/
/* with interrupt processing */
/* */
/* Params: IN Context is really pDX */
/* */
/* Return Value:None */
/* */
/* Operation: */
/* */
/* */
/**PROC-**********************************************************************/
BOOLEAN SynchTerminateAdapter (PVOID Context)
{
PDX pDX = (PDX) Context;
TRACE_EVENT (<XTA);
TerminateAdapter (pDX);
TRACE_EVENT(XTA>);
return (FALSE);
}
/**PROC+**********************************************************************/
/* */
/* Name: SynchEntryPointOpen ( */
/* PDEVICE_OBJECT pDeviceObject */
/* ) */
/* */
/* Purpose: Initialise the next device (from GetDriverSpec) */
/* */
/* Params: IN Context is really pDX */
/* */
/* Return Value:BOOLEAN: True if object opened OK */
/* */
/* Operation: */
/* 1. Copy over pre-initialised data sequences */
/* */
/* */
/**PROC-**********************************************************************/
BOOLEAN SynchEntryPointOpen (PVOID Context)
{
PDX pDX = (PDX) Context;
BOOLEAN rc;
TRACE_EVENT (<XEO);
rc = CAST (InitialiseAdapter (pDX), BOOLEAN);
/* if OK, InitAdapter returns TRUE */
TRACE_EVENT(XEO>);
return (rc);
}
/*****************************************************************************/
/* */
/* Name TerminateAdapter */
/* */
/* Purpose Link Device Driver Hardware Termination. */
/* */
/* Does the physical termination, but leaves assigned resurces */
/* still assigned */
/* */
/* Params IN pDX */
/* */
/* OUT Hardware is cleared down. */
/* */
/*****************************************************************************/
void TerminateAdapter (PDX pDX)
{
/***************************************************************************/
/* Reset the 8255 and 8273. */
/***************************************************************************/
IO_OUT (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273On);
/***************************************************************************/
/* Documentation says use a long delay while resetting! */
/* (but I'm blowed if I can find where it says it. Try 10 microsecs.) */
/***************************************************************************/
KeStallExecutionProcessor (10L);
WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273Off);
WR_N_DELAY (pDX->ADAPTERBASE + AR_8255B, A55_Reset8273Off);
IO_OUT (pDX->ADAPTERBASE + AR_8255C, A55_ResetPortC);
/***************************************************************************/
/* Ensure that an MPCA is completely 'turned off'. */
/***************************************************************************/
if (pDX->ConfigData.MPCAModePort NE 0)
{
IO_OUT (pDX->ConfigData.MPCAModePort, AC_MPCAD); /* MPCAD = disable */
TRACE_DATABYTE (Mpc, AC_MPCAD);
}
/***************************************************************************/
/* Check whether this channel was using the DMA and clear it down. */
/***************************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
StopDMA(pDX); /* stop the channel doing anything */
pDX->GrabbedResources &= ~GRABBEDRESOURCE_GOTDMA;
}
}
/****************************************************************************/
/* */
/* Name TxFSMActionAbort */
/* */
/* Purpose Aborts the current transmission. */
/* */
/* Params IN pDX. Transmitter going presumably. */
/* */
/* OUT Receiver started */
/* */
/****************************************************************************/
void TxFSMActionAbort (PDX pDX)
{
TRACE_EVENT (TAb:);
Write8273Cmd (pDX, pDX->CmdStringAbortTransmit);
TRACE_EVENT (TAb;);
}
/****************************************************************************/
/* */
/* Name TxFSMActionEndError */
/* */
/* Purpose Handles Tx error conditions by recording and retrying */
/* */
/* Updates statistics, */
/* Restarts the transmitter. */
/* */
/* Params IN pDX, 8273 result code in TxResult */
/* */
/* OUT Tx Stats updated, transmitter restarted */
/* */
/****************************************************************************/
void TxFSMActionEndError (PDX pDX)
{
TRACE_EVENT (TEr:);
/*********************************************************************/
/* Check whether DMA is supported and kill the DMA Channel if so. */
/*********************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
StopDMA (pDX);
XASSERT (!pDX->DMAIsActive);
}
/***************************************************************************/
/* Increment the stats count and set the flag to pulse the event */
/***************************************************************************/
pDX->pIR->StatusCount++;
pDX->DPCAction |= DPC_ACTION_PULSE;
/***************************************************************************/
/* Basically we translate the error to an index into the stats array held */
/* in the interface record and increment it. */
/***************************************************************************/
TRACE_DATABYTE (TEb, pDX->TxResult);
if (pDX->TxResult EQ ATxR_ErrTxUnderrun)
{
/*************************************************************************/
/* Check how many times we have tried to resend this frame. If this was */
/* the third attempt then give up, otherwise try again. */
/*************************************************************************/
pDX->TxConsecutiveUnderrunCount++; /* increment count of underruns */
if (pDX->TxConsecutiveUnderrunCount > 2)
{
TxFSMActionEndOK(pDX); /* forget this frame - pretend OK*/
}
else
{
TxFSMEvent (pDX, TxFSMInputStart); /* tell FSM to send again */
}
pDX->pIR->StatusArray[SA_TxUnderrun]++; /* transmitter underrun */
}
else if (pDX->TxResult EQ ATxR_ErrTxCTSDrop)
{
TxFSMActionEndOK (pDX); /* forget this frame - pretend OK*/
pDX->pIR->StatusArray[SA_CTSDrop]++; /* clear to send dropped */
}
else
{
TxFSMActionEndOK (pDX); /* forget this frame - pretend OK*/
pDX->pIR->StatusArray[SA_HardwareError]++;
/* unknown result byte! */
}
TRACE_EVENT (TEr;);
}
/****************************************************************************/
/* */
/* Name TxFSMActionEndOK */
/* */
/* Purpose Frame transmission complete. */
/* */
/* Handles the frame transmission completion actions. */
/* Updates the Tx buffer data. */
/* Starts the next transmission if data is available. */
/* */
/* Params IN pDX */
/* */
/* OUT Tx Data Buffer updated */
/* */
/****************************************************************************/
void TxFSMActionEndOK (PDX pDX)
{
USHORT FreedLength; /* how many bytes freed up from bufr */
TRACE_EVENT (TOk:);
/*********************************************************************/
/* Check whether DMA is supported and kill the DMA Channel if so. */
/*********************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
StopDMA (pDX);
XASSERT (!pDX->DMAIsActive);
}
/*********************************************************************/
/* Clear the count of consecutive transmitter underruns. */
/*********************************************************************/
pDX->TxConsecutiveUnderrunCount = 0;
/***************************************************************************/
/* Read the 'tail' and work out the length of the area freed up by the */
/* completion of this transmission. Divide that by two and add it to the */
/* 'maximum' Tx frame size on the interface. The 'max' is in fact half */
/* the total size of the transmit buffer (with some allowance for length */
/* words and rounding up) because the free area within the buffer can be */
/* broken into at most two non-contiguous sections. */
/***************************************************************************/
FreedLength = CAST(MAKEUSHORT (pDX->pSendBuf[pDX->TxNextToTransmit ], \
pDX->pSendBuf[pDX->TxNextToTransmit+1])
+ 2, /* +2 : freeing up length as well */
USHORT);
pDX->pIR->TxMaxFrSizeNow += (FreedLength + 1) / 2;
/* making sure we round UP! */
XTRACE_ACTION (Tz, pDX->pIR->TxMaxFrSizeNow);
/***************************************************************************/
/* Calculate the new next-to-transmit point, wrapping if necessary */
/***************************************************************************/
pDX->TxNextToTransmit += FreedLength;
if (pDX->TxNextToTransmit >= pDX->TxStartUnusedArea)
{
/*************************************************************************/
/* The last buffer we sent was the right-most in the buffer - so we must */
/* reset the unused area and NextToTranmsit */
/*************************************************************************/
pDX->TxStartUnusedArea = SENDBUF_SIZE;
pDX->TxNextToTransmit = 0;
XTRACE_ACTION (Tu, pDX->TxStartUnusedArea);
}
XTRACE_ACTION (Tt, pDX->TxNextToTransmit);
/***************************************************************************/
/* Now check whether there is more data in the buffer and send it. If */
/* there are no more frames, check whether the application has finished */
/* the current transmission and stop the transmitter if so. */
/***************************************************************************/
if (pDX->TxNextToTransmit NE pDX->TxNextToBuffer)
{
TxFSMEvent (pDX, TxFSMInputStart);
}
else
{
/*************************************************************************/
/* The buffer is empty so we need to stop the transmitter if we are half */
/* duplex and we have indeed sent the poll/final bit (as defined in the */
/* 'C' byte of the last transmit command (ooh, it's so tricky). */
/*************************************************************************/
if (BITSOFF(pDX->LinkOptionsByte, LinkOption_FullDuplex) &&
pDX->CmdStringTransmit[5] & POLLFINL
)
{
TxFSMEvent (pDX, TxFSMInputStop);
}
/*************************************************************************/
/* We have to tell the DLC software when the buffer goes empty. */
/*************************************************************************/
pDX->DPCAction |= DPC_ACTION_PULSE; /* do a PULSE-EVENT from the DPC */
}
TRACE_EVENT (TOk;);
}
/*****************************************************************************/
/* */
/* Name TxFSMActionInvalid */
/* */
/* Purpose Error input to FSM */
/* */
/* This routine should never be called as it implies an */
/* 'impossible state/input combination. This is taken to mean */
/* that adapter status has been incorrectly reported and is */
/* treated as a hardware error. */
/* */
/* Params IN pDX */
/* */
/* OUT Error processing started by RQ FSM input */
/* */
/*****************************************************************************/
void TxFSMActionInvalid (PDX pDX)
{
TRACE_EVENT(T!!!);
pDX->pIR->StatusArray[SA_HardwareError]++;
pDX->pIR->StatusCount++;
pDX->DPCAction |= DPC_ACTION_PULSE;
}
/****************************************************************************/
/* */
/* Name TxFSMActionStart */
/* */
/* Purpose Activates the Transmitter. */
/* */
/* Raise RTS (if not raised already). */
/* Start flag streaming. */
/* Stop the receiver (if not full duplex). */
/* Initiate the first transmission. */
/* */
/* Params IN Data held in Device Object */
/* */
/* OUT Transmitter Active and Transmit command issued. */
/* */
/****************************************************************************/
void TxFSMActionStart (PDX pDX)
{
TRACE_EVENT (TSt:);
/***************************************************************************/
/* Put the transmitter into Flag Stream mode while it is active. */
/***************************************************************************/
pDX->CmdStringSetOpMode[2] |= 1; /* turn on flag stream mode */
Write8273Cmd (pDX, pDX->CmdStringSetOpMode);
/***************************************************************************/
/* Ensure that RTS is up. */
/***************************************************************************/
pDX->CmdStringSetPortB[2] |= 1; /* 1 = RTS line - turn it up */
Write8273Cmd (pDX, pDX->CmdStringSetPortB);
pDX->pIR->V24Out |= IR_OV24RTS; /* record fact that RTS is on! */
/***************************************************************************/
/* We are just starting the transmitter up. If the link is not full */
/* duplex then stop the receiver while we are transmitting. */
/***************************************************************************/
if (BITSOFF(pDX->LinkOptionsByte, LinkOption_FullDuplex))
{
RxFSMEvent (pDX, RxFSMInputHold);
}
/***************************************************************************/
/* Lastly, start the first transmission. */
/***************************************************************************/
TxFSMActionXmitNext (pDX);
TRACE_EVENT (TSt;);
}
/*****************************************************************************/
/* */
/* Name TxFSMActionStop */
/* */
/* Purpose Closes down the transmitter */
/* */
/* Drops RTS (if 2-wire) */
/* Stops flag stream and (if not HDLC) */
/* Re-enables the receiver (if half duplex) */
/* */
/* Params IN pDX */
/* */
/* OUT Receiver started if HDX */
/* */
/*****************************************************************************/
void TxFSMActionStop (PDX pDX)
{
TRACE_EVENT (TZap);
/*********************************************************************/
/* Check whether DMA is supported and kill the DMA Channel if so. */
/*********************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
StopDMA (pDX);
XASSERT (!pDX->DMAIsActive);
}
if (BITSOFF(pDX->LinkOptionsByte, LinkOption_4Wire))
{
/*************************************************************************/
/* Modem 2-wire : Turn off RTS and drop it in the interface flags. */
/*************************************************************************/
pDX->CmdStringSetPortB[2] &= ~1;
pDX->CmdStringResetPortB[2] &= ~1;
Write8273Cmd (pDX, pDX->CmdStringResetPortB);
pDX->pIR->V24Out &= ~IR_OV24RTS; /* tell appl that RTS is off! */
}
/*********************************************************************/
/* Turn off Flag Stream Mode. */
/*********************************************************************/
pDX->CmdStringSetOpMode[2] &= ~1;
pDX->CmdStringResetOpMode[2]&= ~1;
Write8273Cmd (pDX, pDX->CmdStringResetOpMode);
pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA);
/*********************************************************************/
/* Release the receiver if this is a half duplex link. */
/*********************************************************************/
if (BITSOFF(pDX->LinkOptionsByte, LinkOption_FullDuplex))
{
RxFSMEvent (pDX, RxFSMInputRelease);
}
}
/*****************************************************************************/
/* */
/* Name TxFSMActionXmitNext */
/* */
/* Purpose Transmits the Next Frame in the Buffer */
/* */
/* Set up the transmit command */
/* Set up the DMA (if used) */
/* Issue the transmit command. */
/* */
/* Params IN pDX, plus xmitter already active */
/* */
/* NextToTransmit: +0) length of buffered frame (A+C+data) */
/* +1) */
/* +2 A(ddress) */
/* +3 C(ontrol) */
/* */
/* Total buffer taken up is Length + 2. */
/* */
/* OUT Transmission under way. */
/* */
/*****************************************************************************/
void TxFSMActionXmitNext (PDX pDX)
{
USHORT LengthFor8273;
UCHAR *p;
UCHAR TempLo;
UCHAR TempHi;
TRACE_EVENT (TSn:);
/***************************************************************************/
/* Before issuing the transmit command, read in the current Port A value */
/* as we won't be able to if both the transmitter and receiver become */
/* active concurrently. */
/***************************************************************************/
pDX->LastPortA = Write8273Cmd (pDX, pDX->CmdStringReadPortA);
/***************************************************************************/
/* Read the current tail position and check for a buffer wrap (tail = */
/* current length of buffer). */
/***************************************************************************/
if (pDX->TxNextToTransmit >= pDX->TxStartUnusedArea)
{
/*************************************************************************/
/* The last buffer we sent was the right-most in the buffer - so we must */
/* reset the unused area and NextToTranmsit */
/*************************************************************************/
pDX->TxStartUnusedArea = SENDBUF_SIZE;
pDX->TxNextToTransmit = 0;
XTRACE_ACTION (Tu, pDX->TxStartUnusedArea);
XTRACE_ACTION (Tt, pDX->TxNextToTransmit);
}
/***************************************************************************/
/* Found the next frame - read its length and set up Tx Command. */
/***************************************************************************/
p = &pDX->pSendBuf[pDX->TxNextToTransmit];
TempLo = *(p++); /* first two bytes are length*/
TempHi = *(p++);
LengthFor8273 = CAST (TempLo + (TempHi <<8)-2,/* allow for the buffered A+C*/
USHORT);
ASSERT (CAST(LengthFor8273, int ) < pDX->LinkMaxFrameSize);
ASSERT (CAST(LengthFor8273, short) >= 0);
pDX->CmdStringTransmit[2] = LOBYTE(LengthFor8273);
/* and put it into the cmd */
pDX->CmdStringTransmit[3] = HIBYTE(LengthFor8273);
/***************************************************************************/
/* and set up the (buffered) A & C in the command */
/***************************************************************************/
TRC_IN_DW (*p);
pDX->CmdStringTransmit[4] = *(p++); /* next two bytes are A&C */
pDX->CmdStringTransmit[5] = *(p++);
pDX->pTxPIOData = p; /* next is PIO data start pos*/
TRACE_EVENT (TPIO);
TRACE_DWORD (pDX->pTxPIOData);
/***************************************************************************/
/* Now prime the DMA if necessary */
/***************************************************************************/
if (pDX->GrabbedResources & GRABBEDRESOURCE_GOTDMA)
{
PHYSICAL_ADDRESS L;
if (pDX->DMAIsActive)
{
StopDMA (pDX);
XASSERT (!pDX->DMAIsActive);
}
L = pDX->SendBufPhysAddr;
L.LowPart += pDX->TxNextToTransmit + 4;
StartDMA(pDX,
L,
LengthFor8273, /* but 8273 will control how many snt*/
DMACmdRead /* set the mode */
);
XASSERT (pDX->DMAIsActive);
}
/******************************************************************/
/* Lastly, issue the transmit command to the 8273. */
/******************************************************************/
Write8273Cmd (pDX, pDX->CmdStringTransmit);
TRACE_EVENT (TSn;);
}
/****************************************************************************/
/* */
/* Name TxFSMEvent */
/* */
/* Purpose Interprets inputs from the Request handlers and from the */
/* ISR and calls the relevant action routines. */
/* */
/* Params IN pDX, and FSM input */
/* */
/* OUT Action routine called and new state set. */
/* */
/****************************************************************************/
void TxFSMEvent (PDX pDX, int Input)
{
TXFSMENTRY *e = &TxFSM [Input] [pDX->TxFSMCurState];
/***************************************************************************/
/* The FSM consists of a 'state' which is actually the address of the */
/* current FSM 'column'. The 'input' is an offset down the column at */
/* which is located an action routine address and a new state. */
/***************************************************************************/
TRACE_EVENT (<TFE);
ASSERT (pDX->TxFSMCurState < TXFSMSTATECOUNT);
ASSERT (Input < TXFSMINPUTCOUNT);
TRACE_EVENTNAME(TxFSMStateNames[pDX->TxFSMCurState]);
TRACE_EVENTNAME(TxFSMInputNames[Input]);
/***************************************************************************/
/* Reset the state to the new state specified in the FSM table */
/* (Important to do this before the actions because some of them then do */
/* recursive calls to FSMEvent */
/***************************************************************************/
pDX->TxFSMCurState = e->NewState;
ASSERT (e->NewState < TXFSMSTATECOUNT);
TRACE_EVENTNAME(TxFSMStateNames[pDX->TxFSMCurState]);
(*(e->pTxActionRoutine)) (pDX);
TRACE_EVENT (TFE>);
}
/**PROC+**********************************************************************/
/* */
/* Name Write8273Cmd ( */
/* PDX pDX */
/* UCHAR *Cmd; */
/* ); */
/* */
/* Purpose Send out a command array (in <cmd>, <length>, <params> format*/
/* */
/* Params IN pDX - the device extension */
/* IN Cmd - ptr to first char of command */
/* */
/* Return Value If command is READ 8273 PORT A/B return value read, otherwise*/
/* return value is meaningless. */
/* */
/* Operation None */
/* */
/* Notes 1. <length> may be 0 */
/* 2. Write8273Cmd (OS/2 DD precursor of this routine used to */
/* return carry on error. We don't bother as this was only */
/* check in one place. */
/* */
/**PROC-**********************************************************************/
UCHAR Write8273Cmd (PDX pDX, UCHAR *Cmd)
{
int ParameterCount;
UCHAR Result = 0;
TRACE_EVENT (<Cmd);
TRACE_CHAR (*Cmd); /* trace command and first 3 params */
TRACE_CHAR (*(Cmd+2));
TRACE_CHAR (*(Cmd+3));
TRACE_CHAR (*(Cmd+4));
WAITUNTIL(pDX,AS_CMBSY|AS_CMBFF,EQ,0);/* wait for busy and full to go off */
if (pDX->HardwareError) /* stop if previous error or error */
return(0); /* this time */
IO_OUT (pDX->ADAPTERBASE+AR_8273S, /* status reg also command register */
*Cmd++); /* command is first byte */
if (*(Cmd-1) EQ 0x22 || *(Cmd-1) EQ 0x23)
{ /* what we sent out was Read Port A/B*/
WAITUNTIL(pDX,AS_CRBFF,EQ,AS_CRBFF);/* wait for result buff full to go on*/
Result = IO_IN (pDX->ADAPTERBASE+AR_8273P);
}
ParameterCount = *(Cmd++); /* parameter count is next */
while (ParameterCount--)
{
WAITUNTIL (pDX, /* wait for busy to go on, but */
(AS_CMBSY|AS_CMBFF|AS_CPBFF), /* command and param buffs are clear */
EQ, AS_CMBSY);
IO_OUT (pDX->ADAPTERBASE+AR_8273P, /* write params to parameter port */
(UCHAR)*(Cmd++));
}
TRACE_EVENT (Cmd>);
return(Result);
}
/*****************************************************************************/
/* */
/* Name LogDriverError */
/* */
/* Purpose Allocates an error log entry, copies over the given info */
/* and writes it to the error log file. */
/* */
/* Params IN pDeviceObject */
/* IN FinalStatus : NT Status returned to calling service */
/* IN UniqueErrorValue : bottom word is the resource message id */
/* IN MajorFunctionCode : for the current Irp */
/* IN IoControlCode : Ioctl code */
/* */
/*****************************************************************************/
VOID LogDriverError (
PDEVICE_OBJECT pDeviceObject,
NTSTATUS FinalStatus,
ULONG UniqueErrorValue,
UCHAR MajorFunctionCode,
ULONG IoControlCode
)
{
PIO_ERROR_LOG_PACKET ErrorLogEntry;
TRACE_EVENT (<LDE);
ErrorLogEntry = IoAllocateErrorLogEntry(
pDeviceObject,
(UCHAR)sizeof(IO_ERROR_LOG_PACKET)
);
if (ErrorLogEntry NE NULL)
{
ErrorLogEntry->ErrorCode = UniqueErrorValue;
ErrorLogEntry->FinalStatus = FinalStatus;
ErrorLogEntry->UniqueErrorValue = UniqueErrorValue;
ErrorLogEntry->MajorFunctionCode = MajorFunctionCode;
ErrorLogEntry->IoControlCode = IoControlCode;
ErrorLogEntry->SequenceNumber = pDeviceObject->ReferenceCount;
IoWriteErrorLogEntry(ErrorLogEntry);
}
TRACE_EVENT (LDE>);
}
/*****************************************************************************/
/* Notes. */
/* */
/* 1. The OS/2 device driver registers the MPA interrupt 3 as exclusive but */
/* then shares it internally. We can't do this unfortunately on NT because */
/* IoConnectInterrupt requires the device object as a parameter. We will */
/* therefore have to register MPAA interrupts as shared. The problem with */
/* the shared interrupts on OS/2 was that phantom interrupts could be */
/* generated (and, because we didn't have to service any cards, the */
/* interrupt request stayed asserted). As NT is well-designed and tested, */
/* this won't occur so we can use shared interrupts. */
/* */
/* 2. Should we action receive results immediately? As background, we do */
/* action all events immediately except for Transmitter results which are */
/* passed to the DPC routine. NT religious dogma says we should not action */
/* the rx result immediately either, but pass them to the DPC routine. The */
/* danger in doing this is that we will miss the start of the next frame. */
/* This isn't a problem in DMA, because we're just letting it rip. But in */
/* PIO mode, we can be some while getting to restart the receiver if we drop */
/* down to DPC level. So... action al rx interrupts immediately. */
/* */
/* */
/*****************************************************************************/
//on termination, unlock any locked memory and free the MDLs (e.g.
//InterfaceRecordMdl)
//
//need to initialise InterfaceRecordMdl = NULL